/* =========================================================================
   Wheel of Luck
   -------------------------------------------------------------------------
   A mini-game, not a settings form. The wheel is one SVG whose wedges are
   paths and whose rotor spins as a single transform, so a spin is one
   compositor animation regardless of how many slots are on it. Faces sit in a
   separate upright layer so a character's artwork never ends up sideways.
   ========================================================================= */

.qp-wheel-host {
  display: grid;
  justify-items: center;
  gap: 10px;
  width: 100%;
}

.qp-wheel-empty {
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 120px;
  font-size: 40px;
  opacity: .35;
}

/* ---- the rail card -------------------------------------------------------- */

/* The dashboard shows a button, not a wheel. It says one thing -- whether a
   spin is waiting -- and opens the real wheel when tapped. */
.qp-wheel-card {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px;
  border: 1px solid var(--qpt-border, #e7e4ee);
  border-radius: 18px;
  background: var(--qpt-surface, #fff);
  color: var(--qpt-text, #171326);
  text-align: start;
  cursor: pointer;
  transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.qp-wheel-card:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(24, 16, 48, .12); }
.qp-wheel-card:disabled { cursor: default; opacity: .6; }

.qp-wheel-card.is-ready {
  border-color: transparent;
  /* Teal into blue: reads as "go" without the arcade pink, and sits calmly
     beside the rest of the academic dashboard. */
  background: linear-gradient(135deg, #0f9b8e 0%, #1382b8 52%, #2563eb 100%);
  color: #fff;
  box-shadow: 0 10px 28px rgba(16, 130, 184, .32);
}

.qp-wheel-card-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  flex: 0 0 auto;
  border-radius: 15px;
  background: rgba(255, 255, 255, .24);
  font-size: 30px;
  line-height: 1;
}
.qp-wheel-card:not(.is-ready) .qp-wheel-card-icon { background: var(--qpt-surface-alt, #f4f4f8); }
.qp-wheel-card.is-ready .qp-wheel-card-icon { animation: qp-wheel-card-tilt 3.4s ease-in-out infinite; }

@keyframes qp-wheel-card-tilt {
  0%, 82%, 100% { transform: rotate(0deg); }
  86% { transform: rotate(-13deg); }
  92% { transform: rotate(11deg); }
}

.qp-wheel-card-copy { display: grid; gap: 1px; flex: 1 1 auto; min-width: 0; }
.qp-wheel-card-copy b { font-size: 17px; font-weight: 900; letter-spacing: -.01em; }
/* The app-wide `small { color: muted !important }` in the theme would make this
   unreadable on the gradient, so the subtitle is a span that inherits instead. */
.qp-wheel-card-sub { font-size: 12px; opacity: .85; color: inherit; }
.qp-wheel-card-go { font-size: 20px; opacity: .5; }

/* A live dot carries the whole message: a spin is waiting. */
.qp-wheel-card-dot {
  width: 11px;
  height: 11px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 0 rgba(255, 255, 255, .8);
  animation: qp-wheel-card-ping 1.9s ease-out infinite;
}

@keyframes qp-wheel-card-ping {
  0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .75); }
  70%, 100% { box-shadow: 0 0 0 13px rgba(255, 255, 255, 0); }
}

/* ---- the modal ------------------------------------------------------------ */

body.qp-wheel-open { overflow: hidden; }

.qp-wheel-modal {
  position: fixed;
  inset: 0;
  z-index: 2100;
  display: grid;
  place-items: center;
  padding: clamp(10px, 2.5vmin, 28px);
  background: radial-gradient(circle at 50% 40%, rgba(60, 24, 120, .78), rgba(12, 8, 26, .92));
  backdrop-filter: blur(6px);
  animation: qp-wheel-fade .22s ease both;
}

.qp-wheel-modal-card {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  justify-items: center;
  gap: clamp(8px, 2vmin, 18px);
  /* The modal takes most of the screen on every shape of device without ever
     overflowing it: the wheel inside is sized from the smaller viewport axis. */
  width: min(96vw, 760px);
  max-height: 96dvh;
  padding: clamp(14px, 3vmin, 28px);
  border-radius: clamp(18px, 3vmin, 28px);
  background: var(--qpt-surface, #fff);
  box-shadow: 0 40px 110px rgba(8, 4, 22, .55);
  animation: qp-wheel-modal-in .34s cubic-bezier(.34, 1.4, .64, 1) both;
}
.qp-wheel-modal-card:focus { outline: none; }

@keyframes qp-wheel-modal-in {
  from { opacity: 0; transform: translateY(22px) scale(.94); }
  to { opacity: 1; transform: none; }
}

.qp-wheel-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
}
.qp-wheel-modal-head b { font-size: clamp(17px, 2.6vmin, 22px); font-weight: 950; }

.qp-wheel-modal-close {
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 50%;
  background: var(--qpt-surface-alt, #f1f2f7);
  color: var(--qpt-muted, #6f6a7d);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.qp-wheel-modal-close:hover:not(:disabled) { transform: scale(1.08); }
.qp-wheel-modal-close:disabled { opacity: .35; cursor: default; }

.qp-wheel-modal-body { display: grid; place-items: center; width: 100%; min-height: 0; }

/* ---- the stage ------------------------------------------------------------ */

.qp-wheel-empty {
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 120px;
  font-size: 40px;
  opacity: .35;
}

.qp-wheel-stage {
  position: relative;
  display: grid;
  place-items: center;
  /* vmin keeps the wheel inside both axes, so a short landscape window shrinks
     it the same way a narrow portrait one does. */
  width: min(76vmin, 600px, 100%);
  aspect-ratio: 1;
}

/* A soft halo that brightens when a spin is available, so "ready" reads without
   a sentence saying so. */
.qp-wheel-glow {
  position: absolute;
  inset: -8%;
  border-radius: 50%;
  /* A cool halo to match the card, rather than a yellow arcade flare. */
  background: radial-gradient(circle, rgba(37, 99, 235, .26), rgba(37, 99, 235, 0) 68%);
  opacity: 0;
  transition: opacity .3s ease;
  pointer-events: none;
}
.qp-wheel-stage.is-ready .qp-wheel-glow { opacity: 1; animation: qp-wheel-breathe 2.6s ease-in-out infinite; }

@keyframes qp-wheel-breathe {
  0%, 100% { transform: scale(1); opacity: .72; }
  50% { transform: scale(1.05); opacity: 1; }
}

.qp-wheel-rotor {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  /* Promoted once so the spin never repaints the wedges. */
  will-change: transform;
  transform: rotate(0deg);
}

.qp-wheel-svg { width: 100%; height: 100%; overflow: visible; }

.qp-wheel-wedge {
  stroke: #ffffff;
  stroke-width: 2;
  transition: filter .25s ease, opacity .25s ease;
}
.qp-wheel-wedge.is-out { opacity: .38; filter: grayscale(1); }
/* Pastels cannot get much brighter, so the winner is marked by saturating it
   and lifting it off the page instead. */
.qp-wheel-wedge.is-won { filter: saturate(1.7) brightness(1.04) drop-shadow(0 0 10px rgba(37, 99, 235, .45)); }

.qp-wheel-rim {
  fill: none;
  stroke: #ffffff;
  stroke-width: 5;
}

/* ---- wedge faces ---------------------------------------------------------- */

.qp-wheel-faces { position: absolute; inset: 0; pointer-events: none; }

.qp-wheel-face {
  position: absolute;
  /* Placed as a percentage of the wheel, so one markup scales from the small
     admin preview to a full-screen modal with no per-size maths. */
  top: var(--qp-face-y);
  left: var(--qp-face-x);
  display: grid;
  justify-items: center;
  gap: 2px;
  /* The chord across this wedge, so a twelve-slot wheel keeps each label
     inside its own colour. */
  width: var(--qp-face-w, 24%);
  /* A query container, so the artwork inside can be sized from this wedge's
     width instead of the viewport's. */
  container-type: inline-size;
  /* Never rotated with the wedge: artwork and text stay upright all the way
     around the wheel. */
  transform: translate(-50%, -50%);
  /* The wedges are light now, so the labels are ink rather than white-on-colour
     and need no shadow to stay legible. */
  color: #16233b;
  text-align: center;
  transition: transform .3s cubic-bezier(.34, 1.56, .64, 1);
}
.qp-wheel-face.is-out { opacity: .45; }
.qp-wheel-face.is-won { transform: translate(-50%, -50%) scale(1.2); }
.qp-wheel-face b {
  font-size: clamp(12px, 3vmin, 23px);
  font-weight: 950;
  line-height: 1;
  letter-spacing: -.01em;
}
.qp-wheel-face-title {
  max-width: 100%;
  /* Two lines rather than an ellipsis: "Cham..." names no reward at all. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: clamp(10px, 2.1vmin, 17px);
  line-height: 1.1;
}
.qp-wheel-face-icon { font-size: clamp(15px, 3.6vmin, 30px); line-height: 1; }
.qp-wheel-face-icon.big { font-size: clamp(22px, 5.6vmin, 46px); }
/* Character artwork is the best thing on the wheel, so it gets the whole face
   box rather than a fixed pixel size: it grows with the wedge and with the
   screen, and sits on a soft disc so it reads on any wedge colour. */
.qp-wheel-face-art {
  display: grid;
  place-items: center;
  width: min(100%, 104px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, .62);
  box-shadow: 0 2px 8px rgba(22, 35, 59, .14);
  /* An emoji's advance is wider than its font-size, so the glyph is set from
     the container width and stays inside the disc. */
  font-size: min(62cqw, 62px);
  line-height: 1;
}
.qp-wheel-face-art img,
.qp-wheel-face-art svg,
.qp-wheel-face-art canvas { width: 86%; height: 86%; object-fit: contain; }
.qp-wheel-face-art > span {
  width: 100%;
  height: 100%;
  font-size: inherit;
  line-height: 1;
  place-items: center;
  overflow: hidden;
}

/* ---- pointer and spin button --------------------------------------------- */

.qp-wheel-pointer {
  position: absolute;
  top: -2px;
  left: 50%;
  z-index: 3;
  width: 0;
  height: 0;
  transform: translateX(-50%);
  border-left: 13px solid transparent;
  border-right: 13px solid transparent;
  border-top: 21px solid #1d4ed8;
  filter: drop-shadow(0 2px 5px rgba(22, 35, 59, .32));
}

.qp-wheel-spin {
  position: relative;
  z-index: 4;
  width: 34%;
  aspect-ratio: 1;
  border: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, #2f8fd8, #1c6fc4 58%, #1d4ed8);
  color: #fff;
  font: inherit;
  font-size: clamp(12px, 3.4vw, 15px);
  font-weight: 950;
  letter-spacing: .04em;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(22, 35, 59, .28), inset 0 0 0 5px #ffffff;
  transition: transform .14s ease, box-shadow .2s ease;
}
.qp-wheel-spin:hover:not(:disabled) { transform: scale(1.07); }
.qp-wheel-spin:active:not(:disabled) { transform: scale(.96); }
/* A spent wheel still needs a centre. A near-white disc disappeared against
   the pale wedges, so the locked hub is a solid slate with a readable lock. */
.qp-wheel-spin:disabled {
  cursor: default;
  background: radial-gradient(circle at 32% 28%, #9aa6bd, #7c8aa6 58%, #64748b);
  color: #fff;
  box-shadow: 0 6px 16px rgba(22, 35, 59, .22), inset 0 0 0 5px #ffffff;
}
.qp-wheel-spin:disabled .qp-wheel-spin-inner { font-size: 1.7em; }
.qp-wheel-spin.is-spinning { animation: qp-wheel-pulse .7s ease-in-out infinite; }

@keyframes qp-wheel-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}

/* While the wheel is locked, the student sees what they actually won today
   rather than an empty wheel and a date. */
.qp-wheel-today {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 18px 9px 10px;
  border-radius: 999px;
  background: var(--qpt-surface-alt, #eef2f8);
  color: var(--qpt-text, #16233b);
  font-weight: 900;
}
.qp-wheel-today-art {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: #fff;
  font-size: 24px;
  line-height: 1;
}
.qp-wheel-today-art img,
.qp-wheel-today-art svg { width: 84%; height: 84%; object-fit: contain; }
.qp-wheel-today-art > span { width: 100%; height: 100%; font-size: inherit; line-height: 1; place-items: center; }
.qp-wheel-today b { font-size: 17px; }
.qp-wheel-today.is-miss { opacity: .75; }

.qp-wheel-status {
  margin: 0;
  color: var(--qpt-muted, #6f6a7d);
  font-size: 12px;
  font-weight: 850;
  text-align: center;
}

/* ---- confetti ------------------------------------------------------------- */

.qp-wheel-confetti { position: absolute; inset: 0; pointer-events: none; z-index: 5; }
.qp-wheel-confetti i {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 7px;
  height: 11px;
  border-radius: 2px;
  background: var(--qp-c, #f43f5e);
  opacity: 0;
  animation: qp-wheel-confetti .95s cubic-bezier(.2, .7, .3, 1) var(--qp-delay, 0ms) forwards;
}

@keyframes qp-wheel-confetti {
  0% { opacity: 1; transform: translate(-50%, -50%) rotate(var(--qp-a)) translateY(0) scale(.6); }
  100% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--qp-a)) translateY(calc(var(--qp-d) * -1)) scale(1.15); }
}

/* ---- reward popup ---------------------------------------------------------- */

.qp-wheel-prize-backdrop {
  position: fixed;
  inset: 0;
  /* Above the shop gift notice and the message launcher, both of which float
     over the dashboard and would otherwise swallow the dismiss click. */
  z-index: 2200;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(16, 12, 32, .62);
  animation: qp-wheel-fade .2s ease both;
}

@keyframes qp-wheel-fade { from { opacity: 0; } to { opacity: 1; } }

.qp-wheel-prize {
  display: grid;
  justify-items: center;
  gap: 12px;
  width: min(340px, 100%);
  padding: 30px 26px 24px;
  border-radius: 22px;
  background: var(--qpt-surface, #fff);
  color: var(--qpt-text, #171326);
  text-align: center;
  box-shadow: 0 30px 80px rgba(12, 8, 28, .42);
  animation: qp-wheel-prize-in .38s cubic-bezier(.34, 1.56, .64, 1) both;
}
.qp-wheel-prize:focus { outline: none; }

@keyframes qp-wheel-prize-in {
  from { opacity: 0; transform: translateY(18px) scale(.9); }
  to { opacity: 1; transform: none; }
}

.qp-wheel-prize-backdrop.is-win .qp-wheel-prize {
  background:
    radial-gradient(circle at 50% 0%, color-mix(in srgb, var(--qpt-primary, #6428f5) 16%, transparent), transparent 62%),
    var(--qpt-surface, #fff);
}

.qp-wheel-prize-art {
  display: grid;
  place-items: center;
  width: 108px;
  height: 108px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--qpt-primary, #6428f5) 12%, var(--qpt-surface, #fff));
  font-size: 62px;
  line-height: 1;
  animation: qp-wheel-prize-pop .5s cubic-bezier(.34, 1.56, .64, 1) .08s both;
}
.qp-wheel-prize-art img,
.qp-wheel-prize-art svg,
.qp-wheel-prize-art canvas { width: 82%; height: 82%; object-fit: contain; }

.qp-wheel-prize-icon { font-size: 66px; line-height: 1; animation: qp-wheel-prize-pop .5s cubic-bezier(.34, 1.56, .64, 1) .08s both; }
.qp-wheel-prize-badge { font-size: 20px; font-weight: 900; }

@keyframes qp-wheel-prize-pop {
  from { opacity: 0; transform: scale(.5) rotate(-12deg); }
  to { opacity: 1; transform: none; }
}

.qp-wheel-prize-headline { font-size: clamp(26px, 7vw, 38px); font-weight: 950; line-height: 1.1; }
.qp-wheel-prize small { color: var(--qpt-muted, #6f6a7d); font-size: 13px; font-weight: 800; }
.qp-wheel-prize .qp-btn { margin-top: 4px; min-width: 130px; }

/* ---- admin: the configurator ----------------------------------------------- */

.qp-wheel-admin { display: grid; gap: 16px; }
.qp-wheel-admin-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); gap: 18px; align-items: start; }

.qp-wheel-slot-list { display: grid; gap: 8px; }

.qp-wheel-slot {
  display: grid;
  grid-template-columns: auto 34px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--qpt-border, #e7e4ee);
  border-radius: 11px;
  background: var(--qpt-surface, #fff);
}
.qp-wheel-slot.is-off { opacity: .5; }
.qp-wheel-slot.is-broken { border-color: var(--qpt-danger, #c92f45); }
.qp-wheel-slot-swatch { width: 12px; height: 34px; border-radius: 4px; }
.qp-wheel-slot-face { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 8px; background: var(--qpt-surface-alt, #f4f4f8); font-size: 20px; }
.qp-wheel-slot-face img, .qp-wheel-slot-face svg { width: 26px; height: 26px; object-fit: contain; }
.qp-wheel-slot-copy { min-width: 0; display: grid; }
.qp-wheel-slot-copy b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.qp-wheel-slot-copy small { color: var(--qpt-muted, #6f6a7d); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; }
.qp-wheel-slot-move { display: flex; gap: 2px; }
.qp-wheel-slot-actions { display: flex; gap: 4px; align-items: center; }

.qp-wheel-add { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.qp-wheel-add .qp-input, .qp-wheel-add .qp-select { min-height: 38px; }
.qp-wheel-add .qp-input.tiny { width: 96px; }

.qp-wheel-admin-preview {
  display: grid;
  justify-items: center;
  gap: 12px;
  padding: 18px;
  border: 1px solid var(--qpt-border, #e7e4ee);
  border-radius: 14px;
  background: var(--qpt-surface-alt, #f8fafc);
  position: sticky;
  top: 16px;
}
.qp-wheel-admin-preview .qp-wheel-stage { width: min(230px, 100%) !important; }
.qp-wheel-admin-preview .qp-wheel-spin { pointer-events: none; }
.qp-wheel-admin-count { color: var(--qpt-muted, #6f6a7d); font-size: 12px; font-weight: 850; }

.qp-wheel-admin-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.qp-wheel-admin-actions .qp-btn.primary { margin-inline-start: auto; }

/* ---- narrow screens --------------------------------------------------------- */

@media (max-width: 900px) {
  .qp-wheel-admin-layout { grid-template-columns: minmax(0, 1fr); }
  .qp-wheel-admin-preview { position: static; }
}

/* A tablet in portrait has plenty of width but the wheel is still bounded by
   vmin, so it only needs the modal to stop hugging the edges. */
@media (min-width: 700px) and (max-width: 1100px) {
  .qp-wheel-modal-card { width: min(88vw, 700px); }
  .qp-wheel-stage { width: min(68vmin, 520px, 100%); }
}

@media (max-width: 560px) {
  .qp-wheel-slot { grid-template-columns: auto 30px minmax(0, 1fr) auto; row-gap: 8px; }
  .qp-wheel-slot-actions { grid-column: 1 / -1; justify-content: flex-end; }

  /* On a phone the modal is the screen. The wheel takes the width it can get
     rather than a fixed pixel size, which is what made it feel small before. */
  .qp-wheel-modal { padding: 0; }
  .qp-wheel-modal-card {
    width: 100%;
    max-width: none;
    height: 100dvh;
    max-height: none;
    border-radius: 0;
    padding: max(14px, env(safe-area-inset-top, 0px)) 14px max(14px, env(safe-area-inset-bottom, 0px));
  }
  .qp-wheel-stage { width: min(92vw, 88vmin); }
  .qp-wheel-card { padding: 12px 14px; }
}

/* A short landscape phone has almost no height: the wheel follows vmin down and
   the header shrinks with it. */
@media (max-height: 520px) and (orientation: landscape) {
  .qp-wheel-modal-card { gap: 6px; padding: 10px 14px; }
  .qp-wheel-stage { width: min(78vmin, 100%); }
  .qp-wheel-modal-head b { font-size: 15px; }
}

/* ---- motion budget ----------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .qp-wheel-stage.is-ready .qp-wheel-glow,
  .qp-wheel-spin.is-spinning,
  .qp-wheel-confetti i,
  .qp-wheel-prize,
  .qp-wheel-prize-art,
  .qp-wheel-prize-icon { animation: none !important; }
  .qp-wheel-rotor { transition: none !important; }
}

html[data-qp-performance="high"] .qp-wheel-stage.is-ready .qp-wheel-glow,
html[data-qp-performance="high"] .qp-wheel-spin.is-spinning { animation: none; }
