/* ==========================================================================
   PrizePop for WooCommerce — spin-to-win widget stylesheet.

   Self-contained by design: this file references NO external font, NO external
   image and NO external stylesheet. Rendering the widget therefore costs zero
   third-party requests, which is the engine's long-standing self-contained rule
   and the reason it survives on hosts with strict CSP or no outbound access.

   Shipped as authored — no build step, no preprocessor, no minifier. It is
   registered and enqueued via wp_enqueue_style() under the handle
   `prizepop-widget` by PPWC_Public (plan 01-08); nothing here is ever inlined
   into wp_footer (CORE-02).

   Theming: every visual variation happens through the eight custom properties on
   #ppwc-root below. They carry real default values, and Phase 4's branding UI
   (BUILD-02) overrides them rather than rewriting this file.

   The first five are Phase 1's: the accent triplet paints the wheel rim, the
   modal's top bar, the glow halo, the spin button and the confetti; --ppwc-radius
   sets the corner rounding every card and control derives from; --ppwc-font is the
   type stack. Plan 04-04 added the last three, and each one controls something the
   first five could not reach: --ppwc-modal-bg is the modal card's own background,
   --ppwc-seg-text is the colour of the prize labels drawn on the wheel, and
   --ppwc-wheel-size is the wheel's maximum diameter, which every rule that carries
   the diameter reads so that resizing the wheel moves its clip layer with it.

   EVERY ONE OF THE THREE DEFAULTS BELOW IS THE LITERAL IT REPLACED, unchanged, so
   an install that never opens the Design tab renders exactly as it did before this
   phase existed. tests/test-design-tokens.php section B holds these declarations
   and PPWC_Design::defaults() to each other in both directions, so editing one
   without the other turns the suite red rather than silently drifting -- which is
   the same guard the first five have had since plan 04-01.
   ========================================================================== */
#ppwc-root {
  --ppwc-accent: #e5484d;
  --ppwc-accent-dark: #b3282c;
  --ppwc-accent-soft: #fdecec;
  --ppwc-modal-bg: #fff;
  --ppwc-seg-text: #1a1a1a;
  --ppwc-radius: 10px;
  --ppwc-wheel-size: 320px;
  --ppwc-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-family: var(--ppwc-font);
}

/* The source engine hard-coded an RTL writing direction and right text alignment on this rule.
   Both are gone: they were a client-specific assumption, and WordPress already sets the correct
   document direction from is_rtl(). Everything the widget needs to flip is expressed with logical
   properties (inset-inline-end, padding-inline-end, text-align: start) so it follows whatever the
   surrounding document declares — which is what makes Phase 2's RTL/LTR verification (I18N-03)
   cheap rather than a re-layout. */

#ppwc-root,
#ppwc-root * {
  box-sizing: border-box;
}

/* Root sits at the very bottom of wp_footer, deep and far below the fold on any real page —
   which is exactly the heuristic WP Rocket's "Automatic Lazy Rendering" feature (3.17+) uses to
   flag elements for `content-visibility: auto` via a `[data-wpr-lazyrender]` attribute it adds
   automatically, with no manual per-selector exclusion UI in this WP Rocket version. That's fine
   for genuinely below-the-fold static content, but #ppwc-root only ever holds a `position:fixed`
   overlay meant to be visible immediately regardless of scroll position — it will never actually
   scroll "into view" the way lazy-rendered content expects, so it stays permanently collapsed
   (0-size, unrendered) the instant this attribute lands. Confirmed live 2026-08-20: reproduces
   identically in real Chromium AND Firefox engines whenever the request's User-Agent identifies
   as mobile (WP Rocket's mobile-cache HTML only adds the attribute for mobile UAs) — desktop
   never hits this. Higher specificity than WP Rocket's own `[data-wpr-lazyrender]{content-
   visibility:auto}` rule (ID selector beats a bare attribute selector) plus !important as
   insurance against any future WP Rocket rule-order change; this does not disable Lazy Rendering
   site-wide, only exempts this one widget's root.
   The fix was verified across all three independent browser engines with real mobile user agents
   — Chromium (Pixel 5), WebKit (iPhone 13, the genuine Safari/iOS engine rather than a Chromium
   emulation) and Gecko (Firefox with a manual mobile UA) — each reporting the overlay back at
   top:0 at full viewport height with a real tap on the spin button succeeding.
   CORE-10. Do NOT weaken this to a plain `#ppwc-root` rule and do NOT move it into a media query:
   the attribute selector is what raises specificity above WP Rocket's own rule, and the attribute
   only ever appears on a mobile-cache response, so a media query would be scoping the wrong axis. */
#ppwc-root[data-wpr-lazyrender] {
  content-visibility: visible !important;
}

/* `all: unset` is what stops a buyer's theme from restyling the widget's own controls — themes
   routinely set global button/input padding, borders, transforms and font stacks. It is the single
   most likely declaration here to be "cleaned up" by mistake; it is load-bearing. */
#ppwc-root button,
#ppwc-root input {
  all: unset;
  box-sizing: border-box;
  font-family: var(--ppwc-font);
}

/* -------------------------------------------------------------------- */
/* Floating entry badge                                                  */
/* -------------------------------------------------------------------- */
#ppwc-root .ppwc-badge {
  position: fixed;
  bottom: 20px;
  inset-inline-end: 20px;
  z-index: 2147483000;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 12px 18px;
  background: var(--ppwc-accent);
  color: #fff;
  border-radius: var(--ppwc-radius);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
  animation: ppwc-pulse 2.2s ease-out infinite;
  white-space: nowrap;
}

#ppwc-root .ppwc-badge:hover {
  filter: brightness(1.05);
}

@keyframes ppwc-pulse {
  0% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--ppwc-accent) 55%, transparent), 0 6px 18px rgba(0, 0, 0, 0.25);
  }
  70% {
    box-shadow: 0 0 0 14px color-mix(in srgb, var(--ppwc-accent) 0%, transparent), 0 6px 18px rgba(0, 0, 0, 0.25);
  }
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--ppwc-accent) 0%, transparent), 0 6px 18px rgba(0, 0, 0, 0.25);
  }
}

/* -------------------------------------------------------------------- */
/* Overlay + modal shell                                                 */
/* -------------------------------------------------------------------- */
#ppwc-root .ppwc-overlay {
  position: fixed;
  inset: 0;
  z-index: 2147483001;
  background: rgba(10, 10, 10, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

/* The card. Its max-width is the SECOND place the wheel diameter is load-bearing, and expressing it
   as a literal is what would make --ppwc-wheel-size a token that is set and has no computed effect:
   .ppwc-wheel-wrap is clamped by min(100%, ...) against this box, so a wheel asked for at 420px
   (PPWC_Design::SIZE_MAX) inside a 380px card would silently render at the ~336px content width and
   every diameter above that would look identical. The card therefore grows with the wheel it holds.
   The 44px is this rule's own two 22px inline gutters, which is exactly what separates the card's
   width from its content box -- so at the default 320px this resolves to max(380px, 364px) = 380px,
   byte-identical to the literal it replaces, and only a design that genuinely asks for a bigger
   wheel moves it. `width: 100%` above still wins on a phone, so no viewport gets wider. */
#ppwc-root .ppwc-modal {
  position: relative;
  width: 100%;
  max-width: max(380px, calc(var(--ppwc-wheel-size) + 44px));
  max-height: 92vh;
  overflow-y: auto;
  background: var(--ppwc-modal-bg);
  border-radius: calc(var(--ppwc-radius) + 6px);
  padding: 30px 22px 24px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.38);
  animation: ppwc-modal-in .35s cubic-bezier(.34, 1.56, .64, 1);
}

#ppwc-root .ppwc-modal::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 5px;
  border-radius: calc(var(--ppwc-radius) + 6px) calc(var(--ppwc-radius) + 6px) 0 0;
  background: linear-gradient(90deg, var(--ppwc-accent), var(--ppwc-accent-dark));
}

@keyframes ppwc-modal-in {
  from { opacity: 0; transform: scale(.86); }
  to   { opacity: 1; transform: scale(1); }
}

/* Close control sits in the modal's top INLINE-END corner. The source pinned it with `left`,
   which only read as "top-end" because the document was RTL; under a default LTR document that
   same physical value would land it in the top-left, colliding with the headline. */
#ppwc-root .ppwc-close {
  position: absolute;
  top: 10px;
  inset-inline-end: 10px;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: color-mix(in srgb, #000 6%, transparent);
  color: #333;
  font-size: 16px;
  cursor: pointer;
  line-height: 1;
}

#ppwc-root .ppwc-close:hover {
  background: color-mix(in srgb, #000 12%, transparent);
}

/* The shopper's language control (BUILD-05), in the modal's top INLINE-START corner - the opposite
   end from the close control above, so the two can never collide at any width and each one stays on
   its own side of a centred headline. Logical properties only, like every other rule in this file,
   so the pair swaps sides together with the document direction rather than one of them staying put.
   It inherits the campaign's own font and corner radius through the design tokens, so a shop that
   restyled its wheel does not get one control in the plugin's defaults.

   It is only ever in the DOM when the campaign offers two or more languages: the widget's builder
   returns null below that, so there is no rule here for a hidden or empty state and there must not
   be one - an element styled out of sight is still in the accessibility tree. */
#ppwc-root .ppwc-lang {
  position: absolute;
  top: 10px;
  inset-inline-start: 10px;
  max-width: min(40%, 140px);
  padding: 4px 6px;
  border: 1px solid color-mix(in srgb, #000 14%, transparent);
  border-radius: calc(var(--ppwc-radius) / 2);
  background: color-mix(in srgb, #000 4%, transparent);
  color: #333;
  font-family: var(--ppwc-font);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.25;
  cursor: pointer;
}

#ppwc-root .ppwc-lang:hover {
  background: color-mix(in srgb, #000 10%, transparent);
}

/* The two gutters reserve the two top-corner controls, and they are SYMMETRIC on purpose. The
   inline-end one has always reserved the close control's corner. The inline-start one reserves the
   language control's, and because the headline is centred, adding it also re-centres text that the
   single-sided padding had been nudging toward the start by half a gutter. Both are logical, so
   each one follows the control it belongs to when the document direction flips. Physical
   `padding-left` in the source, for the same RTL-only reason. */
#ppwc-root .ppwc-headline {
  margin: 0 0 4px;
  padding-inline-start: 26px;
  padding-inline-end: 26px;
  font-size: 20px;
  font-weight: 800;
  text-align: center;
  color: #1a1a1a;
}

#ppwc-root .ppwc-subhead {
  margin: 0 0 14px;
  text-align: center;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ppwc-accent-dark);
  opacity: 0.85;
}

/* -------------------------------------------------------------------- */
/* The optional email field (ESP-01)                                     */
/* -------------------------------------------------------------------- */
/* THREE RULES A STORE THAT NEVER OPTED IN PAYS NOTHING FOR. Email capture ships off and is opted
   into per campaign, and the widget script creates none of these nodes unless the campaign asked
   for an address — so on the default install these selectors match nothing at all and the modal
   lays out exactly as it did before this phase. That is why nothing below touches an existing
   selector: the "off" path has to stay byte-identical, not merely similar.

   `all: unset` on `#ppwc-root input` near the top of this file strips a text box down to nothing —
   no border, no background, no padding, not even a block display. That is deliberate and it is
   what stops a buyer's theme restyling this field, but it means the rule below has to put back
   EVERYTHING a usable input needs. Deleting a declaration here does not fall back to a browser
   default; there is no default left to fall back to.

   EVERY DECLARATION IS DIRECTION-AGNOSTIC, and that is enforced rather than intended: I18N-03
   verified this widget rendered in both directions and tests/test-i18n-css.php fails the build on
   a physical direction property. The label and the error text align to `start`, so they follow the
   document's reading order under Arabic exactly as they do under French, and the box geometry is
   shorthand, which is symmetric and has no side to get wrong.

   The field is visible and labelled ON PURPOSE — it is the opposite of `.ppwc-hp` immediately
   below, and neither rule may ever be written in terms of the other. */
#ppwc-root .ppwc-email-label {
  display: block;
  margin: 2px 2px 6px;
  text-align: start;
  font-size: 12.5px;
  font-weight: 700;
  color: #1a1a1a;
}

#ppwc-root .ppwc-email-input {
  display: block;
  width: 100%;
  margin: 0;
  padding: 12px 14px;
  text-align: start;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, .18);
  border-radius: var(--ppwc-radius);
  font-size: 15px;
  font-weight: 600;
  color: #1a1a1a;
  cursor: text;
}

#ppwc-root .ppwc-email-input:focus {
  border-color: var(--ppwc-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ppwc-accent) 25%, transparent);
}

#ppwc-root .ppwc-email-input::placeholder {
  color: #9a9a9a;
  font-weight: 500;
  opacity: 1;
}

/* Zero height AND zero margin while it is empty, so the field sits directly above the spin button
   until there is genuinely something to say. `:empty` rather than a hidden state, because the node
   has role="alert" and must stay in the accessibility tree for a screen reader to announce the
   message the moment it is written into it. */
#ppwc-root .ppwc-email-error {
  margin: 6px 2px 0;
  text-align: start;
  font-size: 12.5px;
  font-weight: 700;
  color: #c62828;
}

#ppwc-root .ppwc-email-error:empty {
  margin: 0;
}

/* -------------------------------------------------------------------- */
/* Misc form-adjacent elements (no visible fields anymore — frictionless   */
/* spin, nothing to type. Honeypot stays for bot-catching only.)          */
/* -------------------------------------------------------------------- */
/* display:none, never off-screen positioning. An off-screen honeypot is filled by mobile
   autofill on a field no human ever saw, which trips the bot check on a genuine customer —
   documented as having broken real orders in this codebase family. The widget script also sets
   display:none inline, so the trap holds even if this stylesheet fails to load. */
#ppwc-root .ppwc-hp {
  display: none !important;
}

#ppwc-root .ppwc-msg {
  min-height: 18px;
  margin: 10px 2px 0;
  font-size: 12.5px;
  font-weight: 600;
  color: #c62828;
  text-align: center;
}

/* -------------------------------------------------------------------- */
/* Wheel                                                                 */
/* -------------------------------------------------------------------- */
/* The wheel is sized against its CONTAINER, not against the viewport.
   Found by the rendered RTL/LTR check (plan 02-03, I18N-03): check `no-clip`, viewport `mobile`
   (360px), all three locales. The source engine sized this `min(84vw, 320px)`, which was tuned for
   a full-bleed context. Inside this modal the space actually available is
   min(100vw - 32px, 380px) - 44px of modal padding, so at a 360px viewport 84vw resolves to
   302.4px against a 284px content box. The wheel overflowed by 18px, `margin: auto` collapsed to 0
   because the box was over-constrained, and .ppwc-modal (overflow-y:auto, which per spec makes
   overflow-x auto too) grew a real horizontal scrollbar: measured scrollWidth 344 against
   clientWidth 328. Direction-neutral -- it reproduced identically under LTR and RTL -- but it is
   visual breakage on the single most common viewport this widget is seen at.
   `min(100%, 320px)` resolves against the containing block instead, so the wheel can never be
   wider than the space it is given, and the 12px halo bleed below now lands inside the modal's own
   22px padding gutter rather than past its edge. Tablet and desktop are byte-identical to before:
   both already had a 336px content box, where min(100%, 320px) and min(84vw, 320px) both give 320.

   Plan 04-04 replaced the literal with --ppwc-wheel-size, whose default IS 320px, so every number
   in the paragraph above still holds for an install that never sets a design. The min() stays: the
   token is a MAXIMUM diameter, and a phone viewport must still win over it, which is the whole
   point of the clamp. .ppwc-wheel-clip (inset: 0) and .ppwc-wheel (100%/100%) carry no diameter of
   their own and therefore follow this one by construction -- there is no second hard-coded copy for
   a resize to leave behind (T-04-20). */
#ppwc-root .ppwc-wheel-wrap {
  position: relative;
  width: min(100%, var(--ppwc-wheel-size));
  aspect-ratio: 1 / 1;
  margin: 18px auto 14px;
}

/* Pulsing halo behind the wheel — purely decorative, sits at z-index 0 under everything. */
#ppwc-root .ppwc-wheel-wrap::before {
  content: '';
  position: absolute;
  inset: -12px;
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--ppwc-accent) 40%, transparent) 0%, transparent 72%);
  animation: ppwc-wheel-glow 2.2s ease-in-out infinite;
  z-index: 0;
}

@keyframes ppwc-wheel-glow {
  0%, 100% { opacity: .45; transform: scale(.94); }
  50%      { opacity: 1;   transform: scale(1.05); }
}

#ppwc-root .ppwc-pointer {
  position: absolute;
  top: -6px;
  left: 50%;
  transform: translateX(-50%);
  transform-origin: 50% 0%;
  z-index: 3;
  width: 0;
  height: 0;
  border-left: 12px solid transparent;
  border-right: 12px solid transparent;
  border-top: 20px solid var(--ppwc-accent-dark);
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.35));
}

/* While a spin is in flight, the pointer ticks like it's being brushed by each passing segment
   — reinforces motion even though the pointer itself never actually moves relative to the wheel. */
#ppwc-root .ppwc-wheel-wrap.ppwc-spinning .ppwc-pointer {
  animation: ppwc-pointer-tick 0.12s ease-in-out infinite;
}

@keyframes ppwc-pointer-tick {
  0%, 100% { transform: translateX(-50%) rotate(0deg); }
  50%      { transform: translateX(-50%) rotate(10deg); }
}

#ppwc-root .ppwc-sparkle {
  position: absolute;
  font-size: 15px;
  pointer-events: none;
  z-index: 2;
  animation: ppwc-sparkle-twinkle 1.8s ease-in-out infinite;
  transition: opacity 0.25s ease;
}

#ppwc-root .ppwc-wheel-wrap.ppwc-spinning .ppwc-sparkle {
  opacity: 0;
}

@keyframes ppwc-sparkle-twinkle {
  0%, 100% { opacity: 0.25; transform: scale(0.75) translateY(0); }
  50%      { opacity: 1;    transform: scale(1.2) translateY(-4px); }
}

@media (prefers-reduced-motion: reduce) {
  #ppwc-root .ppwc-wheel-wrap.ppwc-spinning .ppwc-pointer { animation: none !important; }
  #ppwc-root .ppwc-sparkle { animation: none !important; }
}

/* Non-rotating clip layer, sized to exactly overlay .ppwc-wheel-wrap. Needed because .ppwc-wheel
   is a SQUARE box that only LOOKS circular (border-radius) — at any spin angle that isn't a
   multiple of 90deg, the rotated square's corners poke outside its own footprint, and since
   .ppwc-wheel-wrap itself has no overflow:hidden, that overflow used to bubble all the way up to
   .ppwc-modal (overflow-y:auto, which implicitly makes overflow-x auto too per spec), producing a
   real horizontal scrollbar in the modal for the ~4.6s duration of every spin. Clipping here
   instead of on .ppwc-wheel-wrap itself matters: the wrap also hosts the pointer (deliberately
   positioned above its top edge) and the idle sparkles/glow halo (deliberately bleeding past the
   wheel's rim) — clipping the wrap would cut those off too. This layer only wraps the rotating
   .ppwc-wheel, so those stay untouched.  */
#ppwc-root .ppwc-wheel-clip {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  overflow: hidden;
}

#ppwc-root .ppwc-wheel {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 6px solid var(--ppwc-accent-dark);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25), inset 0 0 0 2px rgba(255, 255, 255, 0.6);
  transform: rotate(0deg);
  transition: transform 4.6s cubic-bezier(0.17, 0.67, 0.2, 1.0);
  overflow: hidden;
}

/* Slow diagonal shine sweep, purely decorative, clipped to the circle by overflow:hidden above. */
#ppwc-root .ppwc-wheel::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 42%, rgba(255, 255, 255, .38) 50%, transparent 58%);
  background-size: 260% 260%;
  animation: ppwc-shine 3.8s linear infinite;
  pointer-events: none;
}

@keyframes ppwc-shine {
  0%   { background-position: 220% 220%; }
  100% { background-position: -60% -60%; }
}

@media (prefers-reduced-motion: reduce) {
  #ppwc-root .ppwc-wheel { transition: none !important; }
  #ppwc-root .ppwc-wheel-wrap::before,
  #ppwc-root .ppwc-wheel::after { animation: none !important; }
  #ppwc-root .ppwc-modal { animation: none !important; }
}

#ppwc-root .ppwc-wheel-hub {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 22%;
  height: 22%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: #fff;
  border: 3px solid var(--ppwc-accent-dark);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .25);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
}

/* Radial text (rotated to follow the spoke, like a real prize wheel) — positioned via plain
   polar-to-Cartesian percentages (left/top) computed in JS, with rotation applied in the same
   inline transform. NOT a CSS rotate/transform-origin trick relative to a pre-laid-out strip —
   two earlier drafts got that kind of reference-frame math wrong in different ways and were
   hard to eyeball-verify. This box's exact footprint (46x28px, rotated, at this anchor radius)
   was verified numerically for all 6 segments before shipping: ~9deg of angular margin and
   20px+ of both hub and rim clearance at the smallest realistic wheel size — see the geometry
   check run alongside this change. -webkit-line-clamp caps text at 2 lines unconditionally so
   an unusually long admin-edited label can't silently exceed the verified height budget.
   The left/top values here stay PHYSICAL on purpose: they are the output of the polar geometry,
   not a reading-order decision, and the conic-gradient they must line up with does not flip with
   the document direction either. */
#ppwc-root .ppwc-seg-label {
  position: absolute;
  width: 52px;
  text-align: center;
  pointer-events: none;
  z-index: 1;
}

#ppwc-root .ppwc-seg-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  font-size: 14.5px;
  font-weight: 900;
  line-height: 1.12;
  color: var(--ppwc-seg-text);
  overflow-wrap: break-word;
}

#ppwc-root .ppwc-seg-label.ppwc-seg-on-accent .ppwc-seg-text {
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .2);
}

#ppwc-root .ppwc-spin-btn {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 15px;
  text-align: center;
  background: linear-gradient(135deg, var(--ppwc-accent), var(--ppwc-accent-dark));
  color: #fff;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: .3px;
  border-radius: var(--ppwc-radius);
  cursor: pointer;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--ppwc-accent) 45%, transparent);
  transition: opacity 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
  animation: ppwc-btn-pulse 2.4s ease-in-out infinite;
}

@keyframes ppwc-btn-pulse {
  0%, 100% { box-shadow: 0 6px 16px color-mix(in srgb, var(--ppwc-accent) 45%, transparent); }
  50%      { box-shadow: 0 6px 22px color-mix(in srgb, var(--ppwc-accent) 70%, transparent); }
}

#ppwc-root .ppwc-spin-btn:not([data-disabled="1"]):hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 22px color-mix(in srgb, var(--ppwc-accent) 55%, transparent);
}

#ppwc-root .ppwc-spin-btn:active {
  transform: scale(0.98);
}

#ppwc-root .ppwc-spin-btn[data-disabled="1"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
  animation: none;
}

@media (prefers-reduced-motion: reduce) {
  #ppwc-root .ppwc-spin-btn { animation: none !important; }
}

/* -------------------------------------------------------------------- */
/* Result states                                                         */
/* -------------------------------------------------------------------- */
#ppwc-root .ppwc-result {
  position: relative;
  text-align: center;
  padding-top: 6px;
  overflow: hidden;
}

#ppwc-root .ppwc-watermark {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 6rem;
  font-weight: 900;
  color: color-mix(in srgb, var(--ppwc-accent) 35%, transparent);
  pointer-events: none;
  z-index: 0;
  -webkit-user-select: none;
  user-select: none;
}

#ppwc-root .ppwc-result > * {
  position: relative;
  z-index: 1;
}

#ppwc-root .ppwc-result-emoji {
  font-size: 42px;
  line-height: 1;
  margin-bottom: 6px;
}

#ppwc-root .ppwc-result-title {
  margin: 0 0 6px;
  font-size: 20px;
  font-weight: 800;
  color: #1a1a1a;
}

/* Turned into a bold pill badge — this is the single most important line on the whole win
   screen ("how much did I actually win"), and a plain 15px text line undersold it next to the
   big decorative watermark behind it. */
#ppwc-root .ppwc-result-prize {
  display: inline-block;
  margin: 0 0 16px;
  padding: 8px 24px;
  font-size: 26px;
  font-weight: 900;
  color: #fff;
  background: linear-gradient(135deg, var(--ppwc-accent), var(--ppwc-accent-dark));
  border-radius: 999px;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--ppwc-accent) 45%, transparent);
}

#ppwc-root .ppwc-result-sub {
  margin: 0 0 16px;
  font-size: 13.5px;
  color: #666;
  line-height: 1.5;
}

#ppwc-root .ppwc-coupon-box {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: var(--ppwc-accent-soft);
  border: 1px dashed var(--ppwc-accent-dark);
  border-radius: calc(var(--ppwc-radius) * 0.5 + 4px);
  padding: 10px 12px;
  margin-bottom: 8px;
}

#ppwc-root .ppwc-coupon-code {
  font-family: 'Courier New', Courier, monospace;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: 1px;
  color: #1a1a1a;
  -webkit-user-select: all;
  user-select: all;
}

#ppwc-root .ppwc-copy-btn {
  flex-shrink: 0;
  padding: 8px 12px;
  background: var(--ppwc-accent-dark);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  border-radius: calc(var(--ppwc-radius) * 0.4 + 3px);
  cursor: pointer;
  white-space: nowrap;
}

#ppwc-root .ppwc-copy-btn[data-copied="1"] {
  background: #2e7d32;
}

#ppwc-root .ppwc-expiry {
  margin: 4px 0 18px;
  font-size: 13px;
  font-weight: 600;
  color: #888;
}

/* Terms / exclusion note — a labeled box rather than bare fine print: readable at 12.5px with
   real contrast (#666, not a near-invisible light gray), its own background/border so it reads
   as a distinct piece of information rather than a trailing afterthought crammed under the
   expiry line. Still visually a notch quieter than .ppwc-expiry (no bold, softer border) so it
   doesn't compete with the countdown urgency above it. text-align: start rather than the
   source's physical right, so it follows the document direction. */
#ppwc-root .ppwc-terms {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 0 0 18px;
  padding: 8px 10px;
  background: #f7f7f7;
  border: 1px solid #ececec;
  border-radius: calc(var(--ppwc-radius) * 0.4 + 3px);
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.45;
  color: #666;
  text-align: start;
}

#ppwc-root .ppwc-terms::before {
  content: 'ℹ️';
  flex-shrink: 0;
  font-size: 13px;
  line-height: 1.45;
}

/* Friendly orange rather than black — a solid black CTA right after a "you lost" or "here's
   your code" moment reads as somber/alarming, not inviting a customer back to shop. */
#ppwc-root .ppwc-continue-btn {
  display: block;
  width: 100%;
  padding: 13px;
  background: linear-gradient(135deg, #ff9500, #f57600);
  color: #fff;
  font-size: 14.5px;
  font-weight: 800;
  border-radius: var(--ppwc-radius);
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(245, 118, 0, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

#ppwc-root .ppwc-continue-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(245, 118, 0, 0.45);
}

#ppwc-root .ppwc-continue-btn:active {
  transform: scale(0.98);
}

/* Second-chance CTA reuses the same accent gradient as the spin button itself — it IS another
   spin, so it should read as the primary action, not a secondary/close-style button. */
#ppwc-root .ppwc-retry-btn {
  background: linear-gradient(135deg, var(--ppwc-accent), var(--ppwc-accent-dark));
  box-shadow: 0 6px 16px color-mix(in srgb, var(--ppwc-accent) 45%, transparent);
  animation: ppwc-btn-pulse 2.4s ease-in-out infinite;
}

#ppwc-root .ppwc-skip-link {
  display: block;
  width: 100%;
  margin-top: 10px;
  padding: 4px;
  text-align: center;
  background: transparent;
  color: #999;
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  #ppwc-root .ppwc-retry-btn { animation: none !important; }
}

/* -------------------------------------------------------------------- */
/* Confetti                                                              */
/* -------------------------------------------------------------------- */
#ppwc-root .ppwc-confetti-layer {
  position: absolute;
  top: -10px;
  left: 0;
  right: 0;
  height: 0;
  overflow: visible;
  pointer-events: none;
  z-index: 2;
}

#ppwc-root .ppwc-confetti-piece {
  position: absolute;
  top: 0;
  width: 7px;
  height: 12px;
  opacity: 0;
  animation-name: ppwc-confetti-fall;
  animation-timing-function: ease-in;
  animation-fill-mode: forwards;
}

@keyframes ppwc-confetti-fall {
  0% {
    transform: translateY(-10px) rotate(0deg);
    opacity: 1;
  }
  100% {
    transform: translateY(170px) rotate(360deg);
    opacity: 0;
  }
}
