/* webcore — THE LUCKY WHEEL's own sheet.
 * ===========================================================================
 * Owner-neutral, and TOKEN-ONLY. theme.css is the one sheet in an assembled
 * site allowed to decide a colour; every colour below is a role name, so a
 * palette change is one edit there and none here. `wheel.js` reads the same
 * roles through getComputedStyle, which is what keeps the canvas and the page
 * wearing one palette instead of two.
 *
 * WHY A SHEET OF ITS OWN. The dial is a stage — a bezel, a pawl, a hub, a
 * reveal card — and expressing that in site.css would push wheel-only rules
 * onto every page that loads it.
 *
 * ★ A DECLARATION OUTSIDE A RULE IS SILENT. A dropped selector leaves its
 * declarations at the top level, where a CSS parser discards them without a
 * word (memory law, 2026-08-09 — a background that was never DRAWN, not one
 * that was dimmed). Every block below opens and closes in one place, and
 * tests/frontend/test_wheel_page.py fails on a declaration outside a rule.
 *
 * REDUCED MOTION IS THE FINISHED PICTURE, never a shorter one: the transitions
 * here are removed rather than shortened, and the script lands the dial at its
 * final angle instead of walking to it.
 * ===========================================================================
 */

.iw-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  align-items: start;
}

@media (max-width: 860px) {
  .iw-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------------- the dial */

.iw-stage { min-width: 0; }

.iw-dial {
  position: relative;
  width: 100%;
  max-width: 34rem;
  margin-inline: auto;
  aspect-ratio: 1 / 1;
}

.iw-dial canvas {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--r-round, 50%);
  /* The disc paints its OWN ground: a translucent page background showing
     through the machined cuts turns them into gaps. */
  background: var(--bg-deep);
  box-shadow: none;
}

.iw-dial.is-loading canvas { opacity: .55; }

/* The pawl. Its transform is written by the script every frame, so this rule
   must NOT also transition transform: two animations on one property is not a
   layering, it is a race, and the picture loses. */
.iw-pointer {
  position: absolute;
  top: -1.6%;
  left: 50%;
  width: 8%;
  min-width: 1.75rem;
  margin-left: -4%;
  z-index: 2;
  transform-origin: 50% 12%;
  color: var(--accent);
}

.iw-pointer svg { display: block; width: 100%; height: auto; }

/* The hub: the wheel's own plate, and where the result lands. */
.iw-hub {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26%;
  height: 26%;
  margin: -13% 0 0 -13%;
  border-radius: var(--r-round, 50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .15rem;
  text-align: center;
  padding: .4rem;
  background: var(--surface-2);
  border: 1px solid var(--tm-hair);
  color: var(--fg);
  z-index: 1;
}

.iw-hub .hk {
  font-size: clamp(.55rem, 1.4vw, .7rem);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--fg-mute);
  line-height: 1.1;
}

.iw-hub .hv {
  font-family: var(--font-display);
  font-size: clamp(.9rem, 2.6vw, 1.35rem);
  line-height: 1.05;
  overflow-wrap: anywhere;
}

.iw-hub.is-won {
  border: 1px solid var(--accent);
}

.iw-hub.is-won .hv { color: var(--accent-hi); }

/* THE HUB'S OWN PULSE. The dial itself now holds still the moment it lands
   (wheel.js's shouldRun()) - which fixed the plate visibly disagreeing with
   its own payout, but a wheel that goes completely motionless the instant it
   pays out reads as inert rather than as landed. Two breaths on the ring
   that is already the "you won" marker, cheap CSS on a small element, and
   then it rests - a landing has a moment, not a pulse that runs until the
   player looks away. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes iw-hub-pulse {
    0%, 100% {
      box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent);
    }
    50% {
      box-shadow: 0 0 22px 4px color-mix(in srgb, var(--accent) 45%, transparent);
    }
  }
  .iw-hub.is-won { animation: iw-hub-pulse 1.7s ease-in-out 2; }
}

/* ------------------------------------------------------------- the console */

.iw-console {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.iw-bank {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .7rem .9rem;
  border-radius: var(--r-s);
  background: var(--surface);
  border: 1px solid var(--tm-hair);
}

.iw-bank-k {
  font-size: .74rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.iw-bank-v {
  font-family: var(--font-display);
  font-size: 1.35rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.iw-mute {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-round, 50%);
  cursor: pointer;
  color: var(--fg-dim);
  background: var(--surface-2);
}

.iw-mute:hover { color: var(--fg); }
.iw-mute svg { width: 1.1rem; height: 1.1rem; }
/* One icon, two states: the slash and the waves are the same drawing with one
   of the two hidden, so there is no second SVG to keep in step. */
.iw-mute .iw-mute-off { display: none; }
.iw-mute[data-off="1"] .iw-mute-on { display: none; }
.iw-mute[data-off="1"] .iw-mute-off { display: inline; }
.iw-mute[data-off="1"] { color: var(--fg-mute); }

.iw-state {
  margin: 0;
  min-height: 3.2em;
  line-height: 1.5;
}

.iw-state.calm { color: var(--fg-dim); }
.iw-state.bad { color: var(--bad); }
.iw-cd { font-variant-numeric: tabular-nums; font-weight: 700; }

.iw-actions { display: flex; flex-wrap: wrap; gap: .6rem; }
.iw-spin { min-width: 12rem; font-variant-numeric: tabular-nums; }

/* ★ `hidden` LOSES TO A CLASS THAT SETS display. `.btn` is `inline-flex`, and
   a class selector outranks the `[hidden]` UA rule, so every button this
   console hides was drawn anyway - all three states on screen at once, which
   is exactly the "which one am I looking at" confusion rule 5 of wheel.js
   exists to prevent. Caught in a rendered shot, not by any static gate. */
.iw-actions [hidden],
.iw-award[hidden] { display: none !important; }

/* ------------------------------------------------------------- the reveal */

.iw-award {
  border-radius: var(--r);
  padding: 1rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--tm-hair);
  opacity: 0;
  transform: translateY(.4rem);
  transition: opacity .28s ease, transform .28s ease;
}

.iw-award.in { opacity: 1; transform: none; }

.iw-award-k {
  margin: 0 0 .25rem;
  font-size: .74rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.iw-award-v {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 4vw, 1.9rem);
  line-height: 1.1;
  color: var(--accent-hi);
  overflow-wrap: anywhere;
}

.iw-award-s { margin: .35rem 0 0; color: var(--fg-dim); line-height: 1.5; }

/* -------------------------------------------------------------- the board */

.iw-recent {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .35rem;
}

.iw-recent li {
  display: grid;
  grid-template-columns: minmax(4.5rem, auto) 1fr auto;
  gap: .75rem;
  align-items: baseline;
  padding: .5rem .75rem;
  border-radius: var(--r-xs);
  background: var(--surface);
}

.iw-amt {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent-hi);
}

.iw-who { min-width: 0; overflow-wrap: anywhere; }
.iw-when { font-size: .8rem; color: var(--fg-mute); white-space: nowrap; }

@media (max-width: 520px) {
  .iw-recent li { grid-template-columns: minmax(4rem, auto) 1fr; }
  .iw-when { grid-column: 1 / -1; }
}

/* The owner's parked sentences are never drawn; they exist for the script to
   read. `hidden` already does this — the rule is here so a stylesheet reset
   that drops the attribute's default cannot put a wall of copy on the page. */
.iw-copy { display: none !important; }

@media (prefers-reduced-motion: reduce) {
  .iw-award { transition: none; opacity: 1; transform: none; }
}
