/* ============================================================
   SCREEN: vote (route "lounge"): the world-choice duel.
   Prefix: .vote-*  |  local scene vars: --scene-sky-a/b, --scene-ground,
   --scene-accent (country cards, dark variant per side) and
   --scene-globe-* (right-third globe, light + dark variant).

   ui-fluide pass (layer 1, D-022 dérogation): the sun/cloud/silhouette/
   roofline/landmark/foreground decor below (D-003) is the masked-montage
   placeholder for the 3 voxel scenes vote.js mounts into .vote-scene-art /
   .vote-globe-art -- it approximates each diorama in flat CSS while the
   synchronous bake (200-340ms/scene, D-012) is deferred off the critical
   path. Once vote.js adds .is-baked to a host (the canvas already holds
   real pixels by then), that host's decor is hidden and its canvas fades in
   -- see ".scene-canvas reveal" below, once per scene-art block.
   ============================================================ */

.screen-lounge { display: flex; flex-direction: column; gap: 16px; }

/* ---------- layout: duel column + globe column ---------- */

.vote-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.vote-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.vote-side { min-width: 0; }

@media (min-width: 900px) {
  .vote-layout { grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); }
}

/* ---------- intro ---------- */

.vote-intro { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 6px 8px 2px; }
.vote-intro .kicker { justify-content: center; }
.vote-title { font-size: clamp(24px, 4vw, 38px); max-width: 720px; letter-spacing: -.3px; }
.vote-subtitle { font-size: 15px; font-weight: 700; max-width: 480px; }

/* ---------- duel grid ---------- */

.vote-duel {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-areas: "cardA vs cardB";
  align-items: center;
  gap: 16px;
}

.vote-card--japan { grid-area: cardA; }
.vote-card--italy { grid-area: cardB; }

.vote-card { min-width: 0; }

/* ---------- vs medallion ---------- */

.vote-vs {
  grid-area: vs;
  justify-self: center;
  width: 60px; height: 60px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-weight: 900; font-size: 13px; letter-spacing: .04em;
  color: var(--ink);
  background: linear-gradient(150deg, var(--gold), var(--gold-soft));
  border: 2px solid var(--gold);
  box-shadow: var(--shadow-2), 0 0 0 4px var(--panel);
  z-index: 2;
  animation: vote-vs-beat 4.4s ease-in-out infinite;
}
/* the medallion is the eye's entry point into the duel, one slow heartbeat */
@keyframes vote-vs-beat {
  0%, 100% { transform: scale(1); }
  46% { transform: scale(1.06); }
  58% { transform: scale(1.01); }
}
/* at night a solid gold disc fading into a 15%-alpha gold is a hard, glaring
   edge, the medallion becomes a panel token wearing a gold ring instead (D-002) */
[data-theme="dark"] .vote-vs {
  color: var(--gold);
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--gold) 22%, var(--card) 78%),
    var(--card-2));
  border-color: color-mix(in srgb, var(--gold) 55%, transparent);
}

/* ---------- scene (per-card) ---------- */

.vote-scene {
  /* 400px total so the art band left over above the ~190px text panel lands
     around 200-220px (mockup gives the scene ~55% of the card, text ~45%). */
  min-height: 400px;
  display: flex;
  flex-direction: column;
  transition: border-color .25s ease, box-shadow .25s ease, opacity .25s ease, filter .25s ease;
}

/* `.scene-frame .scene-art` (components.css) makes this layer position:absolute;
   inset:0, covering the WHOLE frame, including the area sitting behind the
   opaque .vote-card-body panel below. Every depth layer here is positioned in
   % of ITS OWN height, so on that full-frame box the bottom ~56% of layers
   (Fuji, roofline, ground) rendered underneath the panel and were never seen.
   Bumping specificity here pulls the art layer out of that absolute cover and
   into the flex flow instead: it now only occupies the band actually visible
   above the panel, so the existing bottom/height percentages resolve against
   real screen space. (browser audit 2026-08-12.) */
.vote-scene .vote-scene-art {
  position: relative;
  inset: auto;
  z-index: 0;
  flex: 1 1 auto;
  min-height: 0;
  background: linear-gradient(180deg,
    var(--scene-sky-a) 0%, var(--scene-sky-b) 58%,
    var(--scene-ground) 58%, var(--scene-ground) 100%);
  transition: background .3s ease;
}

/* dark variant: the destination scene colors from data.js are light pastels,
   so in dark theme this file supplies its own softened indigo/violet/rose
   scene vars instead, redeclared on .vote-scene-art (not .vote-scene, which
   carries the data-injected inline vars) so the override wins without !important.
   Composed from tokens only (ARCHITECTURE.md rule 1): --line-soft
   and a --panel/--card mix sit within a few units of the original literals
   on every channel, so the swap is not a visible regression (the two sides
   share the same indigo backdrop and differ through --scene-accent instead,
   same as the original values did: they were only 4-8 units apart). */
[data-theme="dark"] .vote-card--japan .vote-scene-art {
  --scene-sky-a: var(--line-soft);
  --scene-sky-b: color-mix(in srgb, var(--panel) 75%, var(--card) 25%);
  --scene-ground: var(--panel);
  --scene-accent: var(--warm);
}
[data-theme="dark"] .vote-card--italy .vote-scene-art {
  --scene-sky-a: var(--line-soft);
  --scene-sky-b: color-mix(in srgb, var(--panel) 75%, var(--card) 25%);
  --scene-ground: var(--panel);
  --scene-accent: var(--blue);
}

/* depth layers: sky → sun → clouds → far silhouette → roofline → landmarks → foreground → ground */

.vote-sun {
  position: absolute; top: 12%; right: 16%; z-index: 1;
  width: 26%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--gold) 70%, transparent) 0%, transparent 68%);
  animation: vote-sun-breathe 7s ease-in-out infinite;
}
@keyframes vote-sun-breathe {
  0%, 100% { opacity: .7; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.08); }
}

.vote-cloud {
  position: absolute; z-index: 1;
  height: 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--card) 82%, transparent);
  animation: vote-cloud-drift 26s linear infinite;
}
.vote-cloud-1 { top: 15%; left: -30%; width: 44px; }
.vote-cloud-2 { top: 27%; left: -55%; width: 30px; height: 7px; animation-duration: 34s; animation-delay: -9s; }
@keyframes vote-cloud-drift {
  from { transform: translateX(0); }
  to { transform: translateX(420%); }
}

/* far silhouette: Mt Fuji cone (Japan) or Tuscan hills + cypress (Italy) */

.vote-far { position: absolute; inset: 0; z-index: 2; overflow: hidden; }

.vote-fuji {
  position: absolute; left: 50%; bottom: 26%; transform: translateX(-50%);
  width: 58%; height: 34%;
  background: color-mix(in srgb, var(--scene-accent) 26%, var(--scene-sky-b) 74%);
  clip-path: polygon(50% 0%, 76% 100%, 24% 100%);
  opacity: .6;
}
.vote-fuji::after {
  content: ''; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 32%; height: 26%;
  background: color-mix(in srgb, var(--card) 88%, transparent);
  clip-path: polygon(50% 0%, 100% 100%, 0 100%);
  opacity: .85;
}

.vote-hillrange {
  position: absolute; left: -10%; right: -10%; bottom: 26%; height: 26%;
  background: color-mix(in srgb, var(--scene-accent) 20%, var(--scene-sky-b) 80%);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  opacity: .55;
}
.vote-cypress {
  position: absolute; bottom: 26%;
  width: 5px; border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--scene-accent) 45%, var(--ink) 12%);
  clip-path: polygon(50% 0%, 100% 100%, 0 100%);
  opacity: .5;
}
.vote-cypress-1 { left: 15%; height: 15%; }
.vote-cypress-2 { left: 83%; height: 19%; }

/* roofline: a small distant village skyline shared by both sides */

.vote-roofs { position: absolute; left: 0; right: 0; bottom: 30%; height: 8%; z-index: 3; pointer-events: none; }
.vote-roof {
  position: absolute; bottom: 0; width: 9%;
  background: color-mix(in srgb, var(--scene-ground) 55%, var(--scene-accent) 20%);
  clip-path: polygon(50% 0%, 100% 100%, 0 100%);
  opacity: .5;
}
.vote-roof-1 { left: 8%; height: 65%; }
.vote-roof-2 { left: 20%; height: 100%; }
.vote-roof-3 { left: 68%; height: 55%; }
.vote-roof-4 { left: 80%; height: 82%; }

/* landmarks: secondary (blurred, offset) + primary emblem */

.vote-emblem-back {
  position: absolute; bottom: 33%; z-index: 4;
  font-size: clamp(26px, 4.6vw, 38px);
  opacity: .55; filter: saturate(.85);
  animation-delay: -2s;
}
.vote-card--japan .vote-emblem-back { left: 28%; }
.vote-card--italy .vote-emblem-back { left: 74%; }

.vote-emblem {
  position: absolute; z-index: 5;
  left: 50%; bottom: 30%;
  transform: translateX(-50%);
  font-size: clamp(46px, 8vw, 78px);
  filter: drop-shadow(0 10px 16px color-mix(in srgb, var(--scene-accent) 45%, transparent));
}
/* The shared `floaty` keyframes set `transform` wholesale, which erased the
   translateX(-50%) that centres the emblem, the landmark drifted half its own
   width to the right of the card. Fold the centring into the keyframes.
   (Integration review 2026-08-12.) */
.vote-emblem.floaty { animation-name: vote-emblem-float; }
@keyframes vote-emblem-float {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(-7px); }
}

/* foreground props: cherry blossoms + falling petals (Japan), vespa + sprig (Italy) */

.vote-front { position: absolute; inset: 0; z-index: 6; pointer-events: none; }

.vote-blossom { position: absolute; font-size: 17px; }
.vote-blossom-1 { top: 6%; left: 9%; }
.vote-blossom-2 { top: 13%; left: 19%; font-size: 14px; }

.vote-petal {
  position: absolute; top: 4%;
  width: 6px; height: 6px; border-radius: 60% 40% 60% 40%;
  background: color-mix(in srgb, var(--scene-accent) 70%, var(--card) 30%);
  opacity: 0;
  animation: vote-petal-fall 6s linear infinite;
}
.vote-petal-1 { left: 11%; animation-duration: 5.4s; animation-delay: -.4s; }
.vote-petal-2 { left: 18%; animation-duration: 6.6s; animation-delay: -2.1s; }
.vote-petal-3 { left: 26%; animation-duration: 5.1s; animation-delay: -3.6s; }
.vote-petal-4 { left: 15%; animation-duration: 7s; animation-delay: -1.2s; }
.vote-petal-5 { left: 22%; animation-duration: 6.1s; animation-delay: -4.4s; }
@keyframes vote-petal-fall {
  0% { transform: translate(0, 0) rotate(0deg); opacity: 0; }
  10% { opacity: .85; }
  85% { opacity: .55; }
  100% { transform: translate(14px, 210px) rotate(300deg); opacity: 0; }
}

.vote-sprig { position: absolute; top: 8%; left: 8%; font-size: 19px; }
.vote-vespa { position: absolute; bottom: 9%; right: 9%; font-size: 22px; }

.vote-ground {
  position: absolute; left: 0; right: 0; bottom: 0; height: 4px; z-index: 7;
  background: color-mix(in srgb, var(--scene-accent) 35%, transparent);
}

/* ---------- .scene-canvas reveal (ui-fluide pass, layer 1) ----------
   The voxel diorama (vote-japan.js/vote-italy.js) draws the same subject as
   the CSS decor above (sun, Fuji, roofline, emblem, blossoms) in full voxel
   detail, so the two are never meant to coexist: engine.js appends the
   canvas as a plain DOM sibling with z-index:auto, which paints BELOW the
   decor's explicit z-index:1-7 (CSS2.1 painting order) regardless of source
   order, so simply fading the canvas in would leave it hidden behind its
   own placeholder forever. Once vote.js adds .is-baked (the canvas already
   holds real pixels by then), the decor is dropped from rendering instead
   of fought over z-index -- display:none also stops its own animations
   outright, so nothing keeps nudging the opacity our transition just set. */
.vote-scene-art .scene-canvas {
  opacity: 0;
  transition: opacity .22s cubic-bezier(0, 0, 0.2, 1);
}
.vote-scene-art.is-baked .scene-canvas { opacity: 1; }
.vote-scene-art.is-baked > *:not(.scene-canvas) { display: none; }

/* ---------- card body ---------- */

.vote-card-body {
  position: relative; z-index: 1;
  flex: 0 0 auto;
  display: flex; flex-direction: column; gap: 12px;
  padding: 20px;
  background: color-mix(in srgb, var(--card) 90%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--line-soft);
}

.vote-flag-row { display: flex; align-items: flex-start; gap: 10px; }
.vote-flag-row .flag-lg { margin-top: 2px; }
.vote-flag-row h2 { font-size: 19px; }
.vote-flag-row p { font-size: 12.5px; font-weight: 700; }

.vote-meta { font-size: 12.5px; }
.vote-avatars .avatar { border-color: var(--card); }
.vote-count { font-weight: 800; }
.vote-count b { color: var(--ink-soft); font-size: 13px; }

.vote-btn { width: 100%; }

.vote-chosen-badge {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%;
  padding: 12px 16px;
  border-radius: var(--r-lg);
  font-weight: 800; font-size: 15px;
  background: var(--accent-soft); color: var(--accent);
  border: 1.5px solid var(--accent);
}
.vote-chosen-badge--muted {
  background: var(--bg-2); color: var(--muted);
  border-color: var(--line);
}

/* ---------- voted / other states ---------- */

.vote-card.is-voted .vote-scene {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow-2);
}
.vote-card.is-other .vote-scene {
  opacity: .6;
  filter: saturate(.65);
}

/* ---------- chip (pre-vote) ---------- */

.vote-chip {
  align-self: center;
  font-size: 13.5px;
  animation: vote-chip-in .3s ease both;
}
@keyframes vote-chip-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* ---------- stats bar (post-vote) ---------- */

.vote-stats { padding: 20px; animation: vote-stats-in .35s ease both; }
@keyframes vote-stats-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

.vote-bars { display: flex; flex-direction: column; gap: 12px; margin: 10px 0 12px; }
.vote-bar-row { display: grid; grid-template-columns: 90px 1fr 44px; align-items: center; gap: 10px; }
.vote-bar-label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 800; }
.vote-bar-pct { text-align: right; font-size: 13.5px; }

.vote-total { font-weight: 800; font-size: 14px; }

/* ============================================================
   Globe scene, right third: voxel sphere, orbiting plane, floating
   props, and the Next Stop chest anchored on top of it.
   ============================================================ */

.vote-globe-scene {
  min-height: 380px;
  --scene-globe-sky-a: var(--blue-soft);
  --scene-globe-sky-b: var(--bg-2);
  --scene-globe-ocean: var(--blue);
  --scene-globe-ocean-hi: var(--blue-soft);
  --scene-globe-land: var(--accent);
  --scene-globe-land-hi: var(--accent-soft);
  --scene-globe-ring: var(--gold);
}
[data-theme="dark"] .vote-globe-scene {
  /* same token composition as the two card scenes above, a close match to
     the original literal on every channel (tokens only) */
  --scene-globe-sky-a: color-mix(in srgb, var(--panel) 75%, var(--card) 25%);
  --scene-globe-sky-b: var(--bg);
  --scene-globe-ocean: var(--violet);
  --scene-globe-ocean-hi: var(--violet-soft);
  --scene-globe-land: var(--accent);
  --scene-globe-land-hi: var(--accent-soft);
  --scene-globe-ring: var(--gold);
}

.vote-globe-art {
  background: radial-gradient(130% 95% at 50% 0%, var(--scene-globe-sky-a), var(--scene-globe-sky-b) 74%);
  transition: background .3s ease;
}

/* ---------- .scene-canvas reveal (ui-fluide pass, layer 1) ----------
   Same treatment as .vote-scene-art above: globe.js's voxel sphere replaces
   the CSS stars/cubes/orbit/sphere/explorer/balloons below it, so once
   vote.js marks this host .is-baked, the decor is dropped from rendering
   (not fought over z-index, see the longer comment near .vote-ground) while
   the canvas fades in. .vote-next (the Next Stop chest) is a sibling of
   this element, not a child, so it is never touched by this rule. */
.vote-globe-art .scene-canvas {
  opacity: 0;
  transition: opacity .22s cubic-bezier(0, 0, 0.2, 1);
}
.vote-globe-art.is-baked .scene-canvas { opacity: 1; }
.vote-globe-art.is-baked > *:not(.scene-canvas) { display: none; }

.vote-star {
  position: absolute; width: 3px; height: 3px; border-radius: 50%;
  background: var(--card); opacity: 0;
}
.vote-star-1 { top: 10%; left: 14%; }
.vote-star-2 { top: 18%; left: 62%; width: 4px; height: 4px; }
.vote-star-3 { top: 8%; left: 82%; }
.vote-star-4 { top: 27%; left: 36%; }
[data-theme="dark"] .vote-star {
  opacity: .55;
  animation: vote-star-twinkle 3.6s ease-in-out infinite;
}
[data-theme="dark"] .vote-star-2 { animation-delay: -1.2s; }
[data-theme="dark"] .vote-star-3 { animation-delay: -2s; }
[data-theme="dark"] .vote-star-4 { animation-delay: -.6s; }
@keyframes vote-star-twinkle { 0%, 100% { opacity: .25; } 50% { opacity: .9; } }

.vote-cube {
  position: absolute; width: 14px; height: 14px; border-radius: 4px;
  background: linear-gradient(135deg, var(--scene-globe-land-hi), var(--scene-globe-land));
  box-shadow: var(--shadow-1);
  opacity: .9;
}
.vote-cube-1 { top: 12%; left: 9%; animation-delay: -1s; }
.vote-cube-2 { top: 28%; right: 8%; width: 10px; height: 10px; animation-delay: -2.6s; }

.vote-balloon { position: absolute; font-size: 20px; }
.vote-balloon-1 { top: 8%; right: 15%; animation-delay: -1.6s; }
.vote-balloon-2 { top: 20%; left: 7%; font-size: 15px; animation-delay: -3s; }

.vote-globe-stage {
  position: absolute; left: 50%; top: 44%;
  width: 68%; aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
}

.vote-orbit-wrap { position: absolute; inset: -12%; }
.vote-orbit-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px dashed var(--scene-globe-ring);
  opacity: .55;
}
.vote-orbit-spin { position: absolute; inset: 0; animation: vote-orbit-spin 16s linear infinite; }
.vote-plane {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -50%);
  display: inline-block; font-size: 16px;
  filter: drop-shadow(0 2px 3px color-mix(in srgb, var(--ink) 45%, transparent));
  animation: vote-plane-counter 16s linear infinite;
}
@keyframes vote-orbit-spin { to { transform: rotate(360deg); } }
@keyframes vote-plane-counter { to { transform: translate(-50%, -50%) rotate(-360deg); } }

.vote-sphere {
  position: absolute; inset: 0; border-radius: 50%; overflow: hidden;
  background:
    radial-gradient(circle at 32% 26%, var(--scene-globe-ocean-hi) 0%, transparent 46%),
    radial-gradient(circle at 68% 74%, color-mix(in srgb, var(--scene-globe-ocean) 70%, var(--ink) 20%) 0%, transparent 62%),
    var(--scene-globe-ocean);
  box-shadow:
    inset -14px -10px 24px color-mix(in srgb, var(--ink) 35%, transparent),
    var(--shadow-2);
}
.vote-continent { position: absolute; background: var(--scene-globe-land); opacity: .88; }
.vote-continent-1 { top: 16%; left: 12%; width: 48%; height: 36%; border-radius: 46% 54% 58% 42% / 52% 44% 56% 48%; }
.vote-continent-2 { bottom: 8%; right: 6%; width: 40%; height: 32%; border-radius: 58% 42% 44% 56% / 48% 58% 42% 52%; }
.vote-continent-3 { top: 56%; left: 34%; width: 22%; height: 18%; border-radius: 50%; opacity: .7; }
.vote-sphere-shine {
  position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 30% 22%, color-mix(in srgb, var(--card) 65%, transparent) 0%, transparent 42%);
  mix-blend-mode: overlay;
  animation: vote-shine-breathe 6s ease-in-out infinite;
}
@keyframes vote-shine-breathe { 0%, 100% { opacity: .45; } 50% { opacity: .85; } }

/* sibling of .vote-sphere (not nested inside it) so its raised head/feet
   aren't clipped by the sphere's own overflow:hidden used for continents */
.vote-explorer {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -58%);
  font-size: clamp(20px, 3.6vw, 28px);
  filter: drop-shadow(0 6px 7px color-mix(in srgb, var(--ink) 35%, transparent));
}
/* same conflict as .vote-emblem: `floaty` overwrote translate(-50%, -58%), so
   the explorer slid right and sank into the globe. */
.vote-explorer.floaty { animation-name: vote-explorer-float; }
@keyframes vote-explorer-float {
  0%, 100% { transform: translate(-50%, -58%); }
  50% { transform: translate(-50%, calc(-58% - 7px)); }
}

/* ---------- next stop: anchored over the globe, chest-on-a-hill style ---------- */

.vote-next {
  right: 14px; bottom: 14px; z-index: 5;
  width: min(220px, 64%);
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px;
}
.vote-next-icon {
  flex: none;
  width: 38px; height: 38px; border-radius: var(--r-sm);
  display: grid; place-items: center; font-size: 19px;
  background: var(--gold-soft);
}
.vote-next-body { flex: 1; min-width: 0; }
.vote-next-body h3 { font-size: 15px; margin-top: 2px; }
.vote-next-progress { margin: 8px 0 4px; }
.vote-next-count { font-size: 11.5px; font-weight: 700; }

/* ---------- responsive ---------- */

@media (max-width: 760px) {
  .vote-duel {
    grid-template-columns: 1fr;
    grid-template-areas: "cardA" "vs" "cardB";
    justify-items: center;
  }
  .vote-vs { margin: -6px 0; }
  .vote-card { width: 100%; }
}

@media (max-width: 480px) {
  /* 270px used to be enough padding for a scene that (bugged) never showed
     more than a sliver above the panel anyway. Now that the art band is real
     estate the layers actually render into, it needs to stay tall enough for
     the emblem (min 46px glyph via its clamp()) to clear the roofline. */
  .vote-scene { min-height: 360px; }
  .vote-emblem { bottom: 26%; }
  .vote-globe-scene { min-height: 300px; }
}

@media (prefers-reduced-motion: reduce) {
  .vote-chip, .vote-stats,
  .vote-sun, .vote-cloud, .vote-petal, .vote-star, .vote-vs,
  .vote-orbit-spin, .vote-plane, .vote-sphere-shine,
  /* the base net (base.css) clamps animation-duration to .01ms but never
     animation-iteration-count, so every element sharing components.css's
     infinite `.floaty` keyframe kept looping forever until named here (the
     ui-fluide doctrine: "traiter ses infinies"). .vote-emblem/.vote-explorer
     each carry their own centering override (.vote-emblem.floaty,
     .vote-explorer.floaty) at higher specificity than a bare class would
     reach, so they need the same .foo.floaty compound here to actually win;
     the rest are matched the same way for consistency. */
  .vote-emblem.floaty, .vote-emblem-back.floaty,
  .vote-sprig.floaty, .vote-vespa.floaty,
  .vote-cube-1.floaty, .vote-cube-2.floaty,
  .vote-balloon-1.floaty, .vote-balloon-2.floaty,
  .vote-next-icon.floaty,
  .vote-explorer.floaty {
    animation: none;
  }
  .vote-cloud { opacity: .7; }
  .vote-petal { opacity: 0; }
  /* .vote-next-icon is a SIBLING of .vote-globe-art, not a child, so the
     `.is-baked > *` rule that stops the other decorations never reaches it:
     without the line above it kept looping forever, reduced-motion or not.
     And this one needs the attribute selector to outweigh the dark-theme
     twinkle rule, which is more specific than a bare class. */
  [data-theme="dark"] .vote-star { opacity: .5; animation: none; }
  /* the global net already clamps transition-duration to .01ms, but the
     reveal below is a JS-gated one-shot (nobody awaits its transitionend),
     so cutting it outright (rather than just clamping) makes the canvas
     appear the instant .is-baked lands, no residual fade either way --
     same call as shop.css's equivalent .scene-canvas rule. */
  .vote-scene-art .scene-canvas,
  .vote-globe-art .scene-canvas {
    transition: none;
  }
}
