/**
 * news-vote.css — the Scale: MLNF's article vote control.
 *
 * One control, used on both news surfaces (the feed cards in news.html and the
 * article page in news-article.html) so the two can't drift apart again. It
 * replaces the pair of generic thumbs buttons that lived on the article page.
 *
 * The idea is MLNF's own: vote-display.js already reads a vote score as a
 * tipping scale (fa-scale-balanced / -unbalanced, gold for net-positive, ember
 * for net-negative). That was a passive readout sitting next to the real
 * buttons. Here the scale IS the control:
 *
 *   - The capsule leans. data-nv-lean=up|down|even recolours its hairline and
 *     wash, so a story the hall approved of and one it rejected are different
 *     objects on the page, not the same object with a different digit.
 *   - The net is the verdict, set in the display face. The two tallies are
 *     evidence: they live in the consensus bar, the accessible name and the
 *     Who-voted modal, not as a third and fourth number fighting for the eye.
 *   - The consensus bar under the capsule splits gold/ember in proportion to
 *     the raw tallies. +9 from 12-3 and +9 from 1000-991 are the same digit and
 *     completely different facts; the bar is the only place that difference
 *     shows. A near-even bar means a contested story.
 *
 * Motion is one moment: casting a vote tips the capsule toward the side you
 * chose and lets it settle, and the bar slides to its new split. Nothing else
 * animates. The tip is suppressed under prefers-reduced-motion, where the bar
 * still resizes and the lean colour still changes.
 *
 * Colours are carried as rgb triplets rather than color-mix() because nothing
 * else in this codebase uses color-mix, and an unsupported color-mix drops the
 * whole `border:` shorthand rather than degrading. --gold-rgb exists only in
 * the dark theme block of styles.css, so this file states both themes itself.
 */

.nv {
  /* Gold resolves per theme (#8a6f1a on parchment, #D4AF37 on navy) so approval
     keeps AA on both grounds. Ember has no site token: light mode gets a shade
     dark enough to pass on cream, dark mode gets the vote-display.js ember. */
  --nv-up: #8a6f1a;
  --nv-up-rgb: 138, 111, 26;
  --nv-down: #a83a2a;
  --nv-down-rgb: 168, 58, 42;
  --nv-rest: #6B7280;
  --nv-rest-rgb: 107, 114, 128;
  --nv-lean-rgb: var(--nv-rest-rgb);
  --nv-lean: var(--nv-rest);

  display: inline-flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 0.25rem 0.3rem 0.3rem;
  border: 1px solid rgba(var(--nv-lean-rgb), 0.34);
  border-radius: var(--radius-full, 9999px);
  background: rgba(var(--nv-lean-rgb), 0.07);
  transition: border-color 0.25s ease, background-color 0.25s ease;
  transform-origin: 50% 50%;
  vertical-align: middle;
}

[data-theme="dark"] .nv {
  --nv-up: #D4AF37;
  --nv-up-rgb: 212, 175, 55;
  --nv-down: #c94f3d;
  --nv-down-rgb: 201, 79, 61;
  --nv-rest: #9CA3AF;
  --nv-rest-rgb: 156, 163, 175;
}

.nv[data-nv-lean="up"] { --nv-lean: var(--nv-up); --nv-lean-rgb: var(--nv-up-rgb); }
.nv[data-nv-lean="down"] { --nv-lean: var(--nv-down); --nv-lean-rgb: var(--nv-down-rgb); }

.nv-row {
  display: flex;
  align-items: center;
  gap: 0.1rem;
}

/* ── Buttons ─────────────────────────────────────────────────────────── */
.nv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.1rem;
  min-height: 1.9rem;
  padding: 0 0.4rem;
  border: none;
  border-radius: var(--radius-full, 9999px);
  background: transparent;
  color: var(--nv-rest);
  font: inherit;
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease;
}

.nv-up:hover:not(:disabled),
.nv-up[aria-pressed="true"] { color: var(--nv-up); background: rgba(var(--nv-up-rgb), 0.14); }

.nv-down:hover:not(:disabled),
.nv-down[aria-pressed="true"] { color: var(--nv-down); background: rgba(var(--nv-down-rgb), 0.14); }

.nv-btn:disabled {
  cursor: not-allowed;
  opacity: 0.4;
  background: transparent;
}

.nv-btn:focus-visible,
.nv-scale:focus-visible {
  outline: 2px solid var(--nv-up);
  outline-offset: 2px;
}

/* ── The scale + net verdict ─────────────────────────────────────────── */
.nv-scale {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 1.9rem;
  padding: 0 0.45rem;
  border: none;
  border-radius: var(--radius-full, 9999px);
  background: transparent;
  color: var(--nv-lean);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.25s ease;
}

.nv-scale:hover:not([disabled]) { background: rgba(var(--nv-lean-rgb), 0.12); }
.nv-scale[disabled] { cursor: default; }

.nv-scale > i { font-size: 0.85rem; }

.nv-net {
  font-family: var(--font-display, 'Playfair Display', serif);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

/* ── Consensus bar ───────────────────────────────────────────────────── */
/* Inset from the capsule's own curve so the split's ends read as ends rather
   than as something the border-radius sliced off. */
.nv-bar {
  position: relative;
  height: 2px;
  margin: 0 0.55rem;
  border-radius: 2px;
  background: rgba(var(--nv-rest-rgb), 0.28);
  overflow: hidden;
}

.nv-bar-up {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--nv-up);
  transition: width 0.3s ease;
}

.nv-bar-down {
  position: absolute;
  inset: 0 0 0 auto;
  width: 0;
  background: var(--nv-down);
  transition: width 0.3s ease;
}

/* Nothing cast yet: an empty hairline, not a misleading 50/50 split. */
.nv[data-nv-total="0"] .nv-bar { opacity: 0.45; }

/* ── The one motion moment: the beam tips and settles ────────────────── */
.nv-tip-up { animation: nvTipUp 0.42s cubic-bezier(0.22, 1.2, 0.36, 1); }
.nv-tip-down { animation: nvTipDown 0.42s cubic-bezier(0.22, 1.2, 0.36, 1); }

@keyframes nvTipUp {
  0% { transform: rotate(0deg); }
  35% { transform: rotate(-1.4deg); }
  100% { transform: rotate(0deg); }
}

@keyframes nvTipDown {
  0% { transform: rotate(0deg); }
  35% { transform: rotate(1.4deg); }
  100% { transform: rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
  .nv-tip-up, .nv-tip-down { animation: none; }
}

/* ── Article-page variant: same control, more air ────────────────────── */
.nv--lg { padding: 0.35rem 0.4rem 0.4rem; }
.nv--lg .nv-btn { min-width: 2.6rem; min-height: 2.2rem; font-size: 1rem; }
.nv--lg .nv-scale { min-height: 2.2rem; padding: 0 0.6rem; }
.nv--lg .nv-net { font-size: 1.15rem; }
.nv--lg .nv-scale > i { font-size: 0.95rem; }

/* ── Touch ───────────────────────────────────────────────────────────── */
/* In px, not rem, on purpose: mobile-optimized.css drops the root font to 13px
   on phones, which quietly shrank a 2.5rem target to 32px. Two adjacent
   buttons that small, one meaning approve and one meaning reject, is a
   mis-tap that casts a public vote on someone else's story. The floor has to
   be independent of the root font. */
@media (max-width: 480px) {
  .nv-btn, .nv--lg .nv-btn { min-width: 44px; min-height: 44px; font-size: 1rem; }
  .nv-scale, .nv--lg .nv-scale { min-height: 44px; padding: 0 10px; }
  .nv-net, .nv--lg .nv-net { font-size: 17px; }
  .nv-bar { margin: 0 10px; }
}

/* ── Feed card footer that hosts the control ─────────────────────────── */
/* Sits outside the card's read-link, so no control is nested inside an anchor.
   Above the line you read; below the line you act. */
.news-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 0.6rem var(--space-lg, 1.5rem) var(--space-lg, 1.5rem);
}

.news-card-stats {
  display: flex;
  gap: 1rem;
  font-size: 0.875rem;
  color: var(--text-muted, #6B7280);
}
