/*
  The wheel stage — claymorphism, matching form.css.

  Same two rules as the form:
    raised  = dark shadow bottom-right + white shadow top-left
    matte   = no specular streaks, no metallic ramps, no hard edges
  and no `border` or `outline` anywhere; depth is shadow only.

  SVG has no box-shadow, so the wheel's outer clay pair is applied as two
  stacked drop-shadow() filters on the <svg> itself, along the same 145deg axis
  the card uses. Surface volume inside the disc comes from gradients.

  Rotation comes in two halves. The idle drift is a CSS keyframe animation, so
  it costs the main thread nothing. The real SPIN is driven from wheel.js frame
  by frame, because it needs the exact angle each frame to kick the pointer off
  the pegs and to scale the label smear with speed — a transition would hide
  the angle. wheel.js stops the animation before it takes over; see .rotor.
*/

.stage {
  --clay-hi: #ffffff;
  --clay-lo: rgba(142, 155, 184, .55);
  --clay-lo-soft: rgba(142, 155, 184, .28);

  position: relative;
  z-index: 10;
  display: grid;
  place-items: center;
  min-height: 100dvh;
  /* No padding. It was eating 32px off the width the wheel could use, and the
     brand panel and mute button are position:fixed so they need none. */
  padding: 0;
  font-family: var(--brand-font);
  color: var(--brand-ink);

  /* No backdrop. The 45deg scene sits behind and must stay visible AND
     interactive — it is the tilt/parallax layer. */
  background: none;
  overflow: hidden;
  pointer-events: none;
}

/* Only the controls take input; everything else lets events through so the
   background parallax keeps tracking the cursor across the whole page. */
.stage > *,
.wheel-wrap { pointer-events: none; }
.wheel { pointer-events: auto; }

/* A warm haze under the wheel. Very low contrast — clay is lit, not glowing. */
@keyframes glowBreathe {
  0%, 100% { opacity: .5; }
  50%      { opacity: .82; }
}

.stage__glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(94vw, 640px);
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  /* No blur filter. A radial-gradient is already soft, and blurring a 640px
     element that is simultaneously animating opacity AND transform meant a full
     re-blur every frame. The gradient stops were widened to make up for it. */
  background: radial-gradient(circle, rgba(231, 51, 49, .13) 0%, rgba(247, 166, 0, .08) 40%, rgba(247, 166, 0, .03) 62%, transparent 78%);
  /* Opacity only: it composites, where transform on a huge element does not. */
  animation: glowBreathe 6s ease-in-out infinite;
  will-change: opacity;
  pointer-events: none;
}

/* ------------------------------------------------------------ brandbar */
/* Same clay panel as the prize reveal, so the two read as one family. */

@keyframes brandIn {
  0%   { opacity: 0; transform: translate(-50%, -22px) scale(.8); }
  60%  { opacity: 1; transform: translate(-50%, 4px) scale(1.04); }
  100% { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

.brandbar {
  position: fixed;
  top: 18px;
  left: 50%;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: 10px;
  border-radius: 26px;
  background: linear-gradient(145deg, #FBFBFE, #E6E8F1);
  box-shadow:
    10px 12px 24px rgba(142, 155, 184, .45),
    -8px -9px 20px rgba(255, 255, 255, .92),
    inset 0 5px 11px rgba(255, 255, 255, .9),
    inset 0 -5px 11px rgba(142, 155, 184, .2);
  animation: brandIn .8s cubic-bezier(.34, 1.56, .64, 1) both .15s;
  pointer-events: none;
}

.brandbar__logo {
  width: 58px;
  height: 58px;
  object-fit: contain;
  display: block;
}

@media (max-width: 380px) {
  .brandbar { top: 12px; padding: 8px; border-radius: 22px; }
  .brandbar__logo { width: 48px; height: 48px; }
}

/* ---------------------------------------------------------------- mute */

.mute {
  position: fixed;
  top: 22px;
  inset-inline-end: 18px;
  z-index: 25;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(145deg, #FBFBFE, #E6E8F1);
  color: #6D7484;
  cursor: pointer;
  box-shadow:
    6px 7px 14px rgba(142, 155, 184, .42),
    -5px -5px 12px rgba(255, 255, 255, .92);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .2s;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}

.mute:active {
  transform: scale(.93);
  box-shadow: inset 3px 4px 8px rgba(142,155,184,.45), inset -3px -3px 7px #fff;
}

.mute svg { width: 19px; height: 19px; display: block; }

@media (max-width: 380px) {
  .mute { top: 14px; inset-inline-end: 12px; width: 38px; height: 38px; }
}

/* --------------------------------------------------------------- wheel */

.wheel-wrap {
  position: relative;
  /*
    Deliberately NOT a grid. As a grid with place-items:center its column was
    auto-sized from its content, while .wheel asked for 100% OF that column —
    circular, so the browser used the SVG's intrinsic size instead and the
    wheel rendered at 69% of the wrap. A block child just fills the width.
  */
  display: block;
  /*
    dvh, not vh. On a phone vh measures the LARGE viewport — the full screen
    with the URL bar hidden — so a vh-based size can exceed what is actually on
    screen and get clipped by the stage. dvh tracks the visible area.
  */
  width: min(96vw, 96dvh);
  aspect-ratio: 1;
}

@keyframes wheelFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}

.wheel {
  display: block;
  width: 100%;
  /* aspect-ratio, never height:100% — see the note above .wheel-wrap. */
  aspect-ratio: 1;
  height: auto;
  animation: wheelFloat 6.5s ease-in-out infinite;
  will-change: transform;
}

/* The clay pair. Two drop-shadows on the SVG stand in for the box-shadow an
   HTML element would get: dark down-right, white up-left. */
.wheel svg {
  width: 100%;
  /* The viewBox is square, so the height follows the width. An <svg> whose
     height cannot resolve silently becomes 150px tall and scales its whole
     drawing down to match. */
  aspect-ratio: 1;
  height: auto;
  display: block;
  overflow: visible;
}

/* The wheel's drop shadow is drawn INSIDE the SVG (see #clayDrop). It used to
   be a ::before disc, which is only circular when its box is exactly square —
   and when it was not, it showed as an oval ring around the wheel. */
.wheel { position: relative; }

/*
  The idle drift is a CSS animation, deliberately.

  It used to be a rAF loop writing a --turn custom property. Custom properties
  inherit, so every write invalidated the whole rotor subtree: at 6x CPU
  throttle that measured 5388ms of main thread per 8s — 67% busy — with 478
  style recalcs and 478 layouts, purely to rotate a wheel nobody was touching.
  As a keyframe animation the same motion costs 58ms per 8s: no style recalc,
  no layout, run on the compositor.

  85.71s per revolution == the 4.2 deg/s the old loop produced, so the wheel
  looks exactly the same.
*/
.rotor {
  transform-origin: 0 0;
  will-change: transform;
}

/*
  :not(.rotor--manual) rather than a separate `animation: none !important`
  override. wheel.js hands the spin to the compositor too, by setting its own
  generated @keyframes as an INLINE animation — and an !important author rule
  would beat that inline value and kill the spin outright.
*/
@media (prefers-reduced-motion: no-preference) {
  .rotor:not(.rotor--manual) { animation: rotorDrift 85.71s linear infinite; }
}

@keyframes rotorDrift {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/*
  NO letter-spacing here. Ever.

  WebKit — so every browser on iOS — drops Arabic shaping AND bidi reordering
  on SVG <text> the moment letter-spacing is anything but normal. "مشروب"
  renders as "بورشم": letters in isolated forms, in reverse order. Chromium
  shapes it correctly, so this is invisible unless you test on Safari.

  Measured across eight variants: every one carrying letter-spacing broke,
  every one without it was correct. Adding direction="rtl", xml:lang="ar" or
  unicode-bidi does NOT rescue it — only removing the spacing does.
*/
.slice-label {
  font-family: var(--brand-font);
  font-weight: 500;
  fill: #fff;
}

/* On the pale slices the ink colour carries it — no outline stroke, which
   would fight the matte surface. */
.slice-label--ink { fill: #3E4453; }

/* At speed the labels smear rather than strobe. Opacity is cheap; a real
   motion blur filter over this much SVG is not. */
#labels { transition: opacity .25s linear; }
.is-fast #labels { opacity: .2; }

/* The winning slice swells like pressed clay, then settles. */
@keyframes winPulse {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}

.slice.is-won { animation: winPulse .95s ease-out 2; transform-origin: 0 0; }

/* Before it is armed the wheel is not a control, so it says nothing and does
   not invite a press. */
.wheel { cursor: default; }
.wheel-wrap.is-armed .wheel { cursor: pointer; }

@keyframes armedPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.025); }
}

.wheel-wrap.is-armed .wheel { animation: wheelFloat 6.5s ease-in-out infinite, armedPulse 1.9s ease-in-out infinite; }

/* ------------------------------------------------------------- pointer */

.pointer {
  position: absolute;
  top: -14px;
  left: 50%;
  z-index: 5;
  width: 44px;
  margin-left: -22px;
  transform-origin: 50% 26%;
  filter:
    drop-shadow(4px 6px 10px var(--clay-lo))
    drop-shadow(-3px -4px 8px rgba(255, 255, 255, .8));
  pointer-events: none;
}

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

/* One kick per peg. Short and sharp — a slow recoil would still be swinging
   when the next peg arrives at speed. */
@keyframes pointerKick {
  0%   { transform: rotate(0deg); }
  22%  { transform: rotate(-14deg); }
  100% { transform: rotate(0deg); }
}

.pointer.is-kicked { animation: pointerKick .13s ease-out; }

/* ---------------------------------------------------------- the reveal */
/* A clay panel that floats in the centre, over the wheel. */

.reveal {
  position: fixed;
  inset: 0;
  z-index: 45;
  display: grid;
  place-items: center;
  padding: 20px;
  pointer-events: none;
}

@keyframes scrimIn { from { opacity: 0; } to { opacity: 1; } }

.reveal__scrim {
  position: absolute;
  inset: 0;
  /* backdrop-filter over the whole viewport is one of the most expensive
     things a mobile GPU can be asked for. A slightly heavier translucent wash
     reads the same here and costs a single paint. */
  background: radial-gradient(circle at 50% 50%, rgba(240, 242, 247, .82) 0%, rgba(214, 218, 230, .92) 70%);
  animation: scrimIn .5s ease-out both;
}

@keyframes panelIn {
  0%   { opacity: 0; transform: scale(.55) translateY(28px) rotate(-5deg); filter: blur(12px); }
  58%  { opacity: 1; transform: scale(1.06) translateY(-6px) rotate(2deg); filter: blur(0); }
  100% { opacity: 1; transform: none; }
}

@keyframes panelFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}

.reveal__panel {
  position: relative;
  display: grid;
  justify-items: center;
  width: min(88vw, 360px);
  padding: 28px 26px 24px;
  border-radius: 36px;
  text-align: center;
  background: linear-gradient(145deg, #FBFBFE, #E6E8F1);
  box-shadow:
    22px 26px 50px rgba(142, 155, 184, .55),
    -16px -18px 40px rgba(255, 255, 255, .95),
    inset 0 10px 20px rgba(255, 255, 255, .9),
    inset 0 -10px 20px rgba(142, 155, 184, .22);
  animation:
    panelIn .95s cubic-bezier(.34, 1.56, .64, 1) both,
    panelFloat 5s ease-in-out 1.1s infinite;
}

.reveal__logo {
  width: 72px;
  height: 72px;
  object-fit: contain;
  margin-bottom: 6px;
}

/* Tracking is a Latin device — Arabic is cursive, and spacing it apart severs
   the joins. On Safari "مبروك" came out as "م ب ر و ك". The weight and colour
   already set this apart as a kicker; it does not need tracking too. */
.reveal__kicker {
  margin: 0;
  font-size: 13.5px;
  font-weight: 350;
  color: #8A91A1;
}

.reveal__name {
  margin: 8px 0 10px;
  font-size: clamp(28px, 8vw, 40px);
  font-weight: 500;
  line-height: 1.18;
  background: linear-gradient(145deg, #F4605E 0%, #E73331 45%, #BD192F 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The QR sits in a scooped well — the one PRESSED surface on an otherwise
   raised panel, which is what makes it read as a slot holding a ticket. */
.reveal__qr {
  display: grid;
  justify-items: center;
  margin: 4px 0 14px;
  padding: 14px 14px 11px;
  border-radius: 22px;
  background: #E7E9F1;
  box-shadow:
    inset 6px 6px 12px rgba(142, 155, 184, .55),
    inset -6px -6px 12px #ffffff;
}

.reveal__qr-img {
  width: 176px;
  height: 176px;
  display: block;
  /* The SVG is drawn on transparent, so the well shows through the quiet zone. */
  image-rendering: crisp-edges;
}

.reveal__note {
  margin: 0;
  font-size: 12.5px;
  font-weight: 350;
  color: #7C8496;
  line-height: 1.6;
}

/* --------------------------------------------------------------- fx */

.fx {
  position: fixed;
  inset: 0;
  z-index: 40;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* --------------------------------------------------------- small phones */

@media (max-width: 380px) {
  .wheel-wrap { width: min(97vw, 94dvh); }
}

/* ------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  .stage__glow,
  .wheel,
  .reveal__panel,
  .brandbar { animation: none !important; }
  .stage__glow { opacity: .6; }
}
