/* ============================================================
   Mori Scratch Popup — Storefront Styles
   Design: clean card, 5 campaign themes, sparkle celebration
   ============================================================ */

/* ---- Scrim overlay ---- */

#scratch-popup {
  position: fixed;
  inset: 0;
  /* Reset native <dialog> UA styles so the element behaves as a full-viewport
     flex scrim. Without these, the UA's `margin:auto`, `width/height:fit-content`,
     `max-width/height` and border/padding would shrink the overlay and break the
     centering of the card inside it. */
  width: auto;
  height: auto;
  max-width: none;
  max-height: none;
  margin: 0;
  border: 0;
  color: inherit;
  background: rgba(20, 15, 10, 0.42);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  padding: 16px;
  box-sizing: border-box;
  /* Keep finger-drags on the scrim/card from chaining into the storefront
     scroll (canvas already uses touch-action:none + preventDefault). */
  overscroll-behavior: contain;
}

/* openPopup() parks focus on the dialog itself (tabindex="-1") instead of
   letting showModal() delegate it to the close X. It's a container, never a
   keyboard stop, so suppress the ring a UA would otherwise draw around the whole
   viewport. Focus rings on the real controls (.sp-close, .sp-decline) are
   untouched. */
#scratch-popup:focus,
#scratch-popup:focus-visible { outline: none; }

/* Namespaced `sp-show` (not a bare `.show`): some storefront themes ship a
   generic utility class `.show { display: block !important }` which would
   otherwise override the flex centering below and push the card to the
   top-left corner. The `!important` here defends against any remaining
   theme rule that targets this element's display. */
#scratch-popup.sp-show {
  display: flex !important;
  animation: sp-scrim-in 0.3s ease;
}

/* The modal dialog renders its own backdrop in the top layer; we paint the
   scrim on the dialog element itself, so keep the native backdrop transparent. */
#scratch-popup::backdrop {
  background: transparent;
}

/* ---- Card ---- */

.sp-card {
  /* Default (modern) theme tokens. Per-theme values are applied as inline
     custom properties via JavaScript after config is loaded. */
  --sp-popup-bg:      #ffffff;
  /* Optional background image, layered over the popup colour. Swapped per step
     by JavaScript (applyStepBackground); `none` falls back to the colour. */
  --sp-popup-bg-image: none;
  --sp-accent:        oklch(62% 0.14 35);
  --sp-heading:       #1a1815;
  --sp-body-color:    #7a746b;
  --sp-footer-color:  #a9a39a;
  --sp-close-color:   #9b958d;
  --sp-reward-bg:     #faf9f7;
  --sp-reward-border: #ece6db;
  --sp-code-bg:       #faf9f7;
  --sp-code-border:   #d8d3cb;
  --sp-code-text:     #1a1815;
  --sp-input-bg:      #ffffff;
  --sp-input-border:  #e3ddd3;
  --sp-input-text:    #1a1815;
  --sp-cta-bg:        #1a1815;
  --sp-cta-color:     #ffffff;

  position: relative;
  width: 420px;
  max-width: calc(100vw - 32px);
  background-color: var(--sp-popup-bg);
  background-image: var(--sp-popup-bg-image);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  transition: background-image 0.3s ease;
  border-radius: 18px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  animation: sp-modal-in 0.4s cubic-bezier(0.2, 0.8, 0.3, 1) both;
}

/* ---- Close button ---- */

.sp-close {
  position: absolute;
  /* Corner placement is merchant-configurable: the card carries an
     .sp-close-at-<x>-<y> class that flips which edges the button anchors to,
     while each layout supplies the inset magnitudes through these properties —
     so the takeover's safe-area insets apply to whichever corner is picked. */
  top: var(--sp-close-top, 14px);
  right: var(--sp-close-right, 14px);
  /* Icon-button size is merchant-configurable (Small/Medium/Large) via the
     --sp-close-size custom property set from the popup config; 34px default. */
  width: var(--sp-close-size, 34px);
  height: var(--sp-close-size, 34px);
  border: none;
  background: transparent;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--sp-close-color);
  padding: 0;
  transition: opacity 0.15s, background 0.15s;
  z-index: 2;
}

/* The X icon scales with the button so all three sizes stay proportional. */
.sp-close svg { width: 50%; height: 50%; }

/* Corner placement. The default (right-top) is the base rule above and needs no
   class; each variant releases the edges it doesn't use so the opposite inset
   takes over. Mirrors CLOSE_POSITIONS in popup.js and the admin preview. */
.sp-close-at-left-top .sp-close     { right: auto; left: var(--sp-close-left, 14px); }
.sp-close-at-right-bottom .sp-close { top: auto; bottom: var(--sp-close-bottom, 14px); }
.sp-close-at-left-bottom .sp-close  { top: auto; bottom: var(--sp-close-bottom, 14px); right: auto; left: var(--sp-close-left, 14px); }

.sp-close:hover { opacity: 0.6; }

/* Expand the tap target to a comfortable >=44px even at the Small size, without
   changing the visible icon size (accessibility / mobile). */
.sp-close::before { content: ''; position: absolute; inset: -8px; }

/* Visible keyboard focus ring (only for keyboard users via :focus-visible), so
   the close control is reachable and clearly indicated. */
.sp-close:focus-visible,
.sp-decline:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
#scratch-popup .sp-close:focus-visible { outline: 2px solid var(--sp-close-color) !important; }

/* Over a background image the panel colour can't guarantee contrast, so give the
   X a subtle dark disc + white icon so it stays visible on any photo. Toggled by
   JS (sp-close-over-image on the card) only when an image sits behind the content. */
#scratch-popup .sp-close-over-image .sp-close {
  background: rgba(0, 0, 0, 0.34) !important;
  color: #fff !important;
  border-radius: 50% !important;
}

/* Keep the close button a transparent icon — themes paint bare `button` with a
   background/border that would box the X. ID prefix + !important wins over them. */
#scratch-popup .sp-close,
#scratch-popup .sp-close:hover {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  color: var(--sp-close-color) !important;
}

/* Decline link ("No thanks…"): a low-emphasis text dismissal under the footer.
   Reuses the footer colour so it inherits the theme/merchant override. */
.sp-decline {
  margin: 10px auto 0;
  padding: 8px 12px;
  border: none;
  background: transparent;
  /* Readable secondary link: it's often the only visible way to dismiss (the
     "text only" style has no X), so it uses the body-text colour at full opacity
     rather than the faint footer grey, with a comfortable tap target. Still
     underlined + smaller than the primary action, so it stays secondary. */
  color: var(--sp-body-color);
  font-size: 13px;
  font-family: inherit;
  line-height: 1.3;
  text-decoration: underline;
  text-underline-offset: 2px;
  opacity: 0.9;
  cursor: pointer;
  transition: opacity 0.15s;
}
.sp-decline:hover { opacity: 1; }
#scratch-popup .sp-decline,
#scratch-popup .sp-decline:hover {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  color: var(--sp-body-color) !important;
}

/* ---- Body layout ---- */

.sp-body {
  /* Vertical padding is overridable per-merchant via --sp-body-pad-top/-bottom
     (set inline on #scratch-popup from config); the fallback is this layout's
     default. Horizontal padding stays fixed per layout. */
  padding: var(--sp-body-pad-top, 54px) 40px var(--sp-body-pad-bottom, 36px);
  display: flex;
  flex-direction: column;
  align-items: center;
  box-sizing: border-box;
  flex: 1;
  font-family: var(--sp-font, -apple-system, BlinkMacSystemFont, 'Inter', system-ui, sans-serif);
}

/* Force the merchant font onto every text element. Inheritance from .sp-body
   isn't enough: storefront themes ship rules targeting h2/p/button directly,
   and an element-targeted rule beats an inherited value. The #scratch-popup
   prefix lifts specificity above typical theme selectors. Code value keeps its
   own monospace face (intentionally excluded). */
#scratch-popup .sp-eyebrow,
#scratch-popup .sp-headline,
#scratch-popup .sp-body-text,
#scratch-popup .sp-footer,
#scratch-popup .sp-email-heading,
#scratch-popup .sp-email-sub,
#scratch-popup .sp-birthday-label,
#scratch-popup .sp-disclaimer,
#scratch-popup .sp-disclaimer-text,
#scratch-popup .sp-sms-consent,
#scratch-popup .sp-code-reward-label,
#scratch-popup .sp-code-reward-sub,
#scratch-popup .sp-code-amount,
#scratch-popup .sp-copy-label,
#scratch-popup .sp-cta-btn,
#scratch-popup .sp-checkout-btn,
#scratch-popup .sp-email-input,
#scratch-popup .sp-code-chip {
  font-family: var(--sp-font, -apple-system, BlinkMacSystemFont, 'Inter', system-ui, sans-serif);
}

/* Pin header spacing so storefront themes can't compress the eyebrow/headline/
   body rhythm via their own h2/p margin + line-height rules. Matches (and gives
   a touch more room than) the admin preview. */
#scratch-popup .sp-eyebrow   { margin: 0 0 14px; line-height: 1; }
#scratch-popup .sp-headline  { margin: 0 0 12px; line-height: 1.15; }
#scratch-popup .sp-body-text { margin: 12px 0 26px; line-height: 1.5; }

/* ---- Header text ---- */

.sp-eyebrow {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sp-accent);
  margin: 0 0 14px;
  text-align: center;
}

.sp-headline {
  font-size: 28px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--sp-heading);
  margin: 0 0 12px;
  text-align: center;
}

.sp-body-text {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--sp-body-color);
  text-align: center;
  margin: 0 0 26px;
  max-width: 280px;
}

.sp-footer {
  font-size: 11px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--sp-footer-color);
  text-align: center;
  margin-top: 32px;
}

/* Long, unbroken merchant text (titles, body copy, prize labels) must wrap
   inside the 420px card instead of pushing past it. `anywhere` also breaks
   single long words/URLs. The discount code chip is intentionally excluded
   (.sp-code-value keeps its single-line ellipsis). */
#scratch-popup .sp-eyebrow,
#scratch-popup .sp-headline,
#scratch-popup .sp-body-text,
#scratch-popup .sp-footer,
#scratch-popup .sp-code-reward-label,
#scratch-popup .sp-code-reward-sub,
#scratch-popup .sp-code-amount,
#scratch-popup .sp-won-label,
#scratch-popup .sp-won-amount,
#scratch-popup .sp-won-sub,
#scratch-popup .sp-email-heading,
#scratch-popup .sp-email-sub {
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ---- Scratch zone (canvas + reward layer share the same container) ---- */

.sp-scratch-zone {
  position: relative;
  width: 100%;
  /* 200px is the natural scratch-panel height (the 340:200 default). It's a
     floor, not a fixed height, so when a reward screen's text wraps to more
     lines the panel — and the whole popup — grows to fit instead of clipping. */
  min-height: 200px;
  display: flex;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--sp-reward-border);
}

/* ---- Reward layer: sits below the canvas ---- */
/* In normal flow (not absolute) so its content drives the scratch-zone height
   and the panel grows with long copy. The canvas still overlays it via inset:0. */

.sp-reward {
  position: relative;
  flex: 1;
  /* Allow the flex item to shrink below its content's intrinsic width so long
     text wraps within the panel instead of overflowing the scratch zone. */
  min-width: 0;
  background: var(--sp-reward-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  box-sizing: border-box;
}

/* ---- Code reveal ---- */

.sp-code-content {
  text-align: center;
  width: 100%;
  padding: 0 16px;
  box-sizing: border-box;
  position: relative;
  z-index: 1;
}

.sp-code-reward-label {
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sp-accent);
  margin-bottom: 4px;
}

.sp-code-reward-sub {
  font-size: 11px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--sp-body-color);
  margin-bottom: 8px;
}

.sp-code-amount {
  font-size: 38px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--sp-heading);
  margin-bottom: 10px;
}

.sp-code-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 13px 18px;
  background: var(--sp-code-bg);
  border: 1px dashed var(--sp-code-border);
  border-radius: 10px;
  box-sizing: border-box;
  cursor: pointer;
  font-family: inherit;
  transition: opacity 0.15s;
}

.sp-code-chip:hover { opacity: 0.8; }

.sp-code-value {
  font-family: 'JetBrains Mono', ui-monospace, 'Courier New', monospace;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--sp-code-text);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sp-copy-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sp-body-color);
  transition: color 0.2s;
  white-space: nowrap;
  flex-shrink: 0;
}

.sp-copy-label--copied { color: var(--sp-accent); }

/* ---- Won screen (claim flow) ---- */

.sp-won-content {
  text-align: center;
  width: 100%;
  padding: 0 16px;
  box-sizing: border-box;
  position: relative;
  z-index: 1;
}

.sp-won-label {
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sp-accent);
  margin-bottom: 6px;
}

.sp-won-amount {
  font-size: 36px;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--sp-heading);
  margin-bottom: 10px;
}

.sp-won-sub {
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--sp-body-color);
  max-width: 240px;
  margin: 0 auto 16px;
}

.sp-claim-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 12px;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ---- Email form ---- */

.sp-email-content {
  width: 100%;
  padding: 0 16px;
  box-sizing: border-box;
  position: relative;
  z-index: 1;
}

.sp-email-heading {
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--sp-heading);
  text-align: center;
  margin-bottom: 4px;
}

.sp-email-sub {
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--sp-body-color);
  text-align: center;
  margin-bottom: 14px;
}

.sp-email-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.sp-email-input {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--sp-input-border);
  border-radius: 9px;
  background: var(--sp-input-bg);
  font-size: 13px;
  font-family: inherit;
  color: var(--sp-input-text);
  box-sizing: border-box;
  outline: none;
  transition: border-color 0.15s;
}

.sp-email-input::placeholder {
  color: var(--sp-input-text);
  opacity: 0.4;
}

.sp-email-input:focus { border-color: var(--sp-accent); }
.sp-email-input--error { border-color: #d96b5c; }

/* Pin input colours/size against storefront themes. Themes ship element-targeted
   rules (input, input[type="email"], .field input) that beat the .sp-email-input
   class and override the typed text + background — leaving it unreadable (e.g.
   light text on our white field, or our dark text on a theme's dark field). The
   #scratch-popup prefix lifts specificity above typical theme selectors. Same
   approach as the text-element pinning above. */
#scratch-popup .sp-email-input {
  background: var(--sp-input-bg);
  color: var(--sp-input-text);
  font-size: 13px;
  -webkit-text-fill-color: var(--sp-input-text);
}
#scratch-popup .sp-email-input::placeholder {
  color: var(--sp-input-text);
  -webkit-text-fill-color: var(--sp-input-text);
  opacity: 0.4;
}

/* Browser autofill repaints the field with its own background + text colour
   (often light text we can't read). Hold our colours via an inset box-shadow
   fill and text-fill-color; the long transition defers Chrome's yellow wash. */
#scratch-popup .sp-email-input:-webkit-autofill,
#scratch-popup .sp-email-input:-webkit-autofill:hover,
#scratch-popup .sp-email-input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px var(--sp-input-bg) inset !important;
  -webkit-text-fill-color: var(--sp-input-text) !important;
  transition: background-color 5000s ease-in-out 0s;
}

/* Hide the browser's autofill icons inside the field. */
#scratch-popup .sp-email-input::-webkit-contacts-auto-fill-button,
#scratch-popup .sp-email-input::-webkit-credentials-auto-fill-button {
  visibility: hidden;
  display: none !important;
  pointer-events: none;
  height: 0;
  width: 0;
  margin: 0;
}

.sp-email-error {
  font-size: 11.5px;
  color: #d96b5c;
  margin: 0;
}

/* Birthday: a caption over three side-by-side dropdowns. The selects carry
   .sp-email-input too, so they inherit the field colours, the focus ring, the
   error border and the theme-proofing above — this block only handles the
   three-up layout and the bits a <select> needs that an <input> doesn't. */
.sp-birthday {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

/* Pinned like the other text elements — storefront themes ship span/label rules
   that would otherwise recolour this against our field. One rule rather than a
   base plus an override, so the size is stated once. */
#scratch-popup .sp-birthday-label {
  color: var(--sp-body-color);
  font-size: 11.5px;
  opacity: 0.75;
  text-align: left;
}

.sp-birthday-row {
  display: flex;
  gap: 8px;
  /* Three selects in a row built for two. Wrapping is the escape hatch for a
     popup narrow enough that they can't all hold their floors below — the year
     drops to its own line rather than every month being clipped to "Septem". */
  flex-wrap: wrap;
}

/* min-width:0 because .sp-email-input sets width:100%, which would otherwise
   refuse to shrink below its content and overflow the row on narrow popups. */
#scratch-popup .sp-birthday-select {
  flex: 1;
  min-width: 0;
  /* Themes commonly restyle select with their own appearance/background-image
     arrow; -webkit-appearance keeps the native control so the arrow can't be
     painted over our field colour, and the right padding leaves room for it. */
  -webkit-appearance: none;
  appearance: none;
  padding-right: 28px;
  cursor: pointer;
  /* The arrow, drawn as a background so it tints with the field's text colour
     rather than whatever the theme's select arrow happens to be. */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 15px) calc(50% + 1px), calc(100% - 10px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* Three dropdowns share a row that was built for two, so they are weighted by
   what they have to fit rather than split evenly: "September" is far wider than
   "Day", and an even third would truncate the month while the day sat half
   empty. The ratios set the widths when there is room; the min-widths are floors
   that keep the longest month and a four-digit year readable, and the day — the
   shortest label of the three — is left to absorb the squeeze, since it inherits
   min-width:0 from the rule above. Past that the row wraps. */
#scratch-popup .sp-birthday-select--month { flex: 1.5 1 0; min-width: 8em; }
#scratch-popup .sp-birthday-select--year  { flex: 1.1 1 0; min-width: 4.75em; }

/* The unpicked state reads as a placeholder, matching the empty email field
   beside it, so an optional birthday doesn't look like a filled-in one.
   :has(option[value=""]:checked) rather than :invalid, which never fires on a
   select without `required` — and rather than a class the JS keeps in sync, which
   is state that can drift from the value it describes. The placeholder option
   always exists, so this cannot go stale. On a browser without :has() an unpicked
   dropdown simply isn't dimmed, which is cosmetic only. */
#scratch-popup .sp-birthday-select:has(option[value=""]:checked) {
  opacity: 0.55;
}

/* Native option lists are painted by the OS, not the popup — without this they
   inherit the field's colour variables and can render unreadable (e.g. our light
   text on the system's white dropdown). */
#scratch-popup .sp-birthday-select option {
  color: initial;
  background: initial;
}

/* The SMS opt-in row shares the disclaimer's look — same size, same alignment —
   because they sit next to each other and reading as two different kinds of
   control would suggest one is more binding than the other. It is always a
   checkbox, so it has no "--text" variant. */
.sp-disclaimer,
.sp-sms-consent {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--sp-body-color);
  text-align: left;
}

.sp-sms-consent {
  cursor: pointer;
}

/* Phone step's skip link: the decline link's look, tightened to sit inside the
   form under the button rather than under the footer. */
.sp-phone-step-skip {
  margin: 0 auto;
  padding: 4px 12px;
  font-size: 12.5px;
}

.sp-disclaimer--text {
  justify-content: center;
  text-align: center;
}

.sp-disclaimer-checkbox {
  flex-shrink: 0;
  margin: 1px 0 0;
  accent-color: var(--sp-accent);
  cursor: pointer;
}

.sp-cta-btn {
  width: 100%;
  padding: 11px;
  background: var(--sp-cta-bg);
  color: var(--sp-cta-color);
  border: none;
  border-radius: 9px;
  font-size: 14px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  letter-spacing: 0.01em;
  transition: opacity 0.15s;
}

.sp-cta-btn:hover { opacity: 0.88; }

/* Pin the interactive buttons against storefront themes. Themes (and other
   installed apps) match bare `button`, `.btn`, `[type="submit"]` etc. and paint
   their own gradient `background-image`, border, `box-shadow`, `filter` and
   `color` over ours. The #scratch-popup prefix adds an ID to specificity, and
   !important on each visual property guarantees our look wins on the base and
   every interactive state. (Layout props stay in the unprefixed rules above.) */
#scratch-popup .sp-cta-btn,
#scratch-popup .sp-checkout-btn {
  background: var(--sp-cta-bg) !important;
  background-image: none !important;
  color: var(--sp-cta-color) !important;
  border: none !important;
  box-shadow: none !important;
  filter: none !important;
  text-shadow: none !important;
}
#scratch-popup .sp-cta-btn:hover,
#scratch-popup .sp-checkout-btn:hover {
  background: var(--sp-cta-bg) !important;
  color: var(--sp-cta-color) !important;
  opacity: 0.88 !important;
}
#scratch-popup .sp-code-chip {
  background: var(--sp-code-bg) !important;
  background-image: none !important;
  border: 1px dashed var(--sp-code-border) !important;
  box-shadow: none !important;
  filter: none !important;
}

/* ---- Canvas (on top of reward layer) ---- */

.sp-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: grab;
  border-radius: 14px;
  touch-action: none;
  transition: opacity 0.55s ease;
  z-index: 2;
}

.sp-canvas.revealed {
  opacity: 0;
  pointer-events: none;
}

/* ---- Sparkle celebration overlay ---- */

.sp-sparkles {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 2;
}

@keyframes sp-twinkle {
  0%   { transform: scale(0) rotate(0deg);  opacity: 0; }
  40%  { transform: scale(1) rotate(35deg); opacity: 1; }
  100% { transform: scale(0) rotate(70deg); opacity: 0; }
}

/* ---- Checkout button (inside reward panel, below code chip) ---- */

.sp-checkout-btn {
  width: 100%;
  margin-top: 8px;
  padding: 9px;
  background: var(--sp-cta-bg);
  color: var(--sp-cta-color);
  border: none;
  border-radius: 9px;
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: opacity 0.15s;
}

.sp-checkout-btn:hover { opacity: 0.88; }

/* ---- Animations ---- */

@keyframes sp-scrim-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes sp-modal-in {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0)    scale(1);    }
}

/* ---- Responsive ---- */

@media (max-width: 480px) {
  .sp-body {
    padding: var(--sp-body-pad-top, 44px) 24px var(--sp-body-pad-bottom, 28px);
  }
  .sp-headline {
    font-size: 22px;
  }
  .sp-body-text {
    font-size: 13px;
    margin-bottom: 20px;
  }
  .sp-code-amount {
    font-size: 30px;
  }
  .sp-code-chip {
    padding: 11px 14px;
  }
  .sp-code-value {
    font-size: 13px;
  }
}

/* ==========================================================================
   Fullscreen layout — displayMode "fullscreen" adds .sp-fullscreen to the
   dialog and the popup becomes a full-viewport takeover: the theme background
   (color or image) paints the whole screen and the same content renders as a
   centered column. Everything is keyed off this one class; the markup and
   flow logic are identical to the classic card.
   ========================================================================== */

#scratch-popup.sp-fullscreen {
  padding: 0;
  /* The card paints the whole viewport, so the dimmed-glass scrim behind it is
     never visible — skip the blur work. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

#scratch-popup.sp-fullscreen .sp-card {
  width: 100%;
  height: 100%;
  max-width: none;
  border-radius: 0;
  /* Long content (timer bar + wrapped copy on short screens) scrolls inside
     the takeover instead of clipping under the card's overflow:hidden. */
  overflow-y: auto;
}

/* Content column: fullscreen changes the stage, not the card's internals. The
   column keeps a readable width and centers vertically, and the scratch panel
   keeps a stable width so the canvas needs no resize handling. */
#scratch-popup.sp-fullscreen .sp-body {
  flex: 1;
  justify-content: center;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  padding: var(--sp-body-pad-top, 72px) 32px var(--sp-body-pad-bottom, 48px);
}

#scratch-popup.sp-fullscreen .sp-scratch-zone {
  max-width: 440px;
  /* The wider 440 panel needs a taller floor to keep ~1.7:1, or it looks
     stretched wide and short. Mobile drops back to 200 below. */
  min-height: 260px;
}

/* Roomier type on the big stage. */
#scratch-popup.sp-fullscreen .sp-eyebrow   { font-size: 12px; }
#scratch-popup.sp-fullscreen .sp-headline  { font-size: 44px; margin-bottom: 16px; }
#scratch-popup.sp-fullscreen .sp-body-text { font-size: 17px; max-width: 420px; }
#scratch-popup.sp-fullscreen .sp-footer    { font-size: 12px; }
#scratch-popup.sp-fullscreen .sp-won-amount,
#scratch-popup.sp-fullscreen .sp-code-amount { font-size: 44px; }
#scratch-popup.sp-fullscreen .sp-won-label,
#scratch-popup.sp-fullscreen .sp-code-reward-label { font-size: 12px; }

/* A larger close target floating on the open stage. It keeps the theme colour
   even with a topbar timer (.sp-has-topbar) because on the takeover the bar is
   an in-column banner, not chrome behind the button. */
#scratch-popup.sp-fullscreen .sp-close {
  /* A takeover fills the viewport to every edge, so the X clears the device
     status bar / notch / home indicator instead of colliding with them. Set as
     insets rather than top/right directly, so whichever corner the merchant
     picked gets the same treatment. Plain values here are the fallback; the
     @supports block below layers the safe-area insets on top where env()/max()
     are understood. (The two-declaration fallback trick doesn't work on custom
     properties: an unparsed max() would still be stored, then fail at
     substitution time and leave the inset `auto`.) */
  --sp-close-top: 22px;
  --sp-close-bottom: 22px;
  --sp-close-right: 24px;
  --sp-close-left: 24px;
  /* Larger on the takeover, but still honours the merchant's size choice. */
  width: calc(var(--sp-close-size, 34px) + 12px);
  height: calc(var(--sp-close-size, 34px) + 12px);
  color: var(--sp-close-color);
}
@supports (top: max(1px, env(safe-area-inset-top, 0px))) {
  #scratch-popup.sp-fullscreen .sp-close {
    --sp-close-top: max(22px, calc(env(safe-area-inset-top, 0px) + 12px));
    --sp-close-bottom: max(22px, calc(env(safe-area-inset-bottom, 0px) + 12px));
    --sp-close-right: max(24px, env(safe-area-inset-right, 0px));
    --sp-close-left: max(24px, env(safe-area-inset-left, 0px));
  }
}

/* Topbar timer: a bar pinned to the viewport's top edge would sit far from the
   vertically centered content (and span the split layout's media panel), so on
   the takeover it joins the content column as a rounded banner above the
   eyebrow. Same markup and JS — only the positioning changes. */
#scratch-popup.sp-fullscreen .sp-timer-topbar {
  position: relative;
  width: 100%;
  max-width: 440px;
  height: 38px;
  border-radius: 10px;
  flex-shrink: 0;
  margin-bottom: 26px;
}

@media (max-width: 480px) {
  #scratch-popup.sp-fullscreen .sp-body { padding: var(--sp-body-pad-top, 56px) 22px var(--sp-body-pad-bottom, 32px); }
  #scratch-popup.sp-fullscreen .sp-headline { font-size: 30px; }
  #scratch-popup.sp-fullscreen .sp-body-text { font-size: 15px; }
  #scratch-popup.sp-fullscreen .sp-won-amount,
  #scratch-popup.sp-fullscreen .sp-code-amount { font-size: 38px; }
  /* Narrow screens: the panel is viewport-wide, so 200 keeps it from getting
     too tall (matches the classic 340x200 aspect). */
  #scratch-popup.sp-fullscreen .sp-scratch-zone { min-height: 200px; }
}

/* ---- Split variant: the fullscreen takeover plus a media panel beside the
   content (displayMode "split" adds .sp-split on top of .sp-fullscreen). The
   panel shows the campaign background image — which moves off the card, so the
   content column stays on the plain theme colour — and falls back to the
   theme's scratch-panel colour when no image is set.

   Every split override is scoped to wide screens, so on narrow screens the
   dialog collapses to the plain fullscreen takeover (panel hidden, background
   image back on the card) purely by inheriting the .sp-fullscreen rules. ---- */

.sp-split-media { display: none; }

@media (min-width: 769px) {
  #scratch-popup.sp-split .sp-split-media {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 45%;
    background-color: var(--sp-scratch-cover, #bdb5a6);
    /* A merchant image when set; otherwise a soft diagonal sheen over the panel
       colour so the panel reads as designed, not a flat block. */
    background-image: var(--sp-popup-bg-image, none), linear-gradient(135deg, rgba(255,255,255,0.18), rgba(255,255,255,0) 42%, rgba(0,0,0,0.24));
    background-size: cover;
    background-position: center;
    transition: background-image 0.3s ease;
  }

  #scratch-popup.sp-split .sp-card { background-image: none; }

  #scratch-popup.sp-split .sp-body {
    margin: 0 0 0 45%;
    width: 55%;
    max-width: none;
    padding: var(--sp-body-pad-top, 64px) 40px var(--sp-body-pad-bottom, 48px);
  }
}

/* ==========================================================================
   Teaser Tab — a tab anchored to an edge of the page that re-opens the
   scratch popup while the visitor still hasn't scratched. Collapsed/idle by
   default; slides in when shown. Colors are driven by CSS custom properties
   (set per campaign in JS) and placement by an `sp-placement-*` modifier class.
   ========================================================================== */
/* ---- Base (shared by every look) ---- */
/* The outer button only positions + slides the launcher; the visible pill/
   circle lives in .sp-teaser-body so its idle motion (bob/pulse/peek) never
   fights the placement transform. Mirrors the design's root + .lc-anim split. */
.sp-teaser-tab {
  --sp-teaser-bg: #1a1815;
  --sp-teaser-text: #ffffff;
  --sp-teaser-attn: #E8896B;
  --sp-teaser-attn-border: #ffffff;
  --sp-teaser-badge-bg: #D9952A;
  --sp-teaser-badge-text: #1B1410;
  /* Tints derived from the background color (recomputed in JS per campaign):
     colored drop shadow, gradient stops (bubble) and glass fill/border. */
  --sp-teaser-shadow: rgba(26, 24, 21, 0.55);
  --sp-teaser-grad-a: rgb(46, 44, 41);
  --sp-teaser-grad-b: rgb(0, 0, 0);
  --sp-teaser-glass-a: rgba(26, 24, 21, 0.3);
  --sp-teaser-glass-b: rgba(26, 24, 21, 0.12);
  --sp-teaser-glass-border: rgba(26, 24, 21, 0.55);
  --sp-teaser-glass-shadow: rgba(26, 24, 21, 0.5);
  position: fixed;
  z-index: 9998;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--sp-teaser-text);
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: transform 0.36s cubic-bezier(0.3, 0.8, 0.3, 1), opacity 0.3s ease, visibility 0.3s ease, filter 0.15s ease, padding 0.2s ease;
  -webkit-font-smoothing: antialiased;
  /* The launcher is a div with role="button" (it can hold the dismiss button,
     which a <button> could not nest). Restate the UA button defaults the looks
     relied on, so the theme's body text styling can't reach in. box-sizing
     matters most: the side tab caps a long label with max-height/max-width,
     and on content-box those caps would exclude its 22px padding and let the
     tab grow past them. */
  box-sizing: border-box;
  line-height: normal;
  text-align: center;
  user-select: none;
  -webkit-user-select: none;
  /* Every look sets the size of its own visible text (the tab on this root,
     the pill on its body, the bubble's label is clipped and its badge is
     explicit), so this only pins what is left — but as a div the launcher
     would otherwise inherit the merchant theme's body size, and a theme with
     a 10px or 20px base would be free to reach anything we forget. */
  font-size: 16px;
}
.sp-teaser-tab.sp-show { opacity: 1; visibility: visible; }
.sp-teaser-tab.sp-show:hover { filter: brightness(1.08); }

/* By default the body is transparent to layout (tab look draws on the button).
   The floating looks turn it into the visible, animated launcher. */
.sp-teaser-body { display: contents; }

/* Gift glyph — shown on every look that carries one; each sets its own size. */
.sp-teaser-icon { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.sp-teaser-icon svg { display: block; width: 22px; height: 22px; }

/* "No icon": the merchant dropped the glyph, leaving a label-only launcher.
   display:none takes the slot out of the flex row entirely, so the row's gap
   collapses with it and the label centres on its own. Never set on the Bold
   bubble, whose circle holds nothing else. */
.sp-teaser-no-icon .sp-teaser-icon { display: none; }

/* Attention cue (pinging dot or "Win" badge) — hidden unless a look opts in. */
.sp-teaser-attn { display: none; }
.sp-teaser-badge { display: none; }

/* Idle motion + attention ripple. Disabled for reduced-motion visitors. */
@keyframes sp-teaser-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes sp-teaser-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.045); } }
@keyframes sp-teaser-peek {
  0%, 84%, 100% { transform: translateY(0) rotate(0deg); }
  89% { transform: translateY(-8px) rotate(-4deg); }
  93% { transform: translateY(-3px) rotate(3deg); }
  97% { transform: translateY(0) rotate(0deg); }
}
@keyframes sp-teaser-ping { 0% { transform: scale(1); opacity: 0.65; } 70%, 100% { transform: scale(2.4); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .sp-teaser-body, .sp-teaser-ping { animation: none !important; }
}

/* Attention effects turned off by the merchant: no idle motion, no dot/badge.
   Set per campaign in JS (sp-teaser-attn-off) — the quiet-launcher option. */
.sp-teaser-attn-off .sp-teaser-body { animation: none !important; }
.sp-teaser-attn-off .sp-teaser-attn { display: none !important; }

/* ---- Dismiss control ----
   A small × that hides the launcher for the rest of the session. Rendered
   only when the merchant turns the option on (sp-teaser-dismissible, set per
   campaign in JS). It sits OUTSIDE .sp-teaser-body on purpose: the body is
   what bobs/pulses/peeks, and a hit target that moves under the cursor is a
   hit target visitors miss. Mirrors TabClose / FloatClose in the admin's
   Launcher.jsx. */
.sp-teaser-close { display: none; }
.sp-teaser-dismissible .sp-teaser-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  line-height: 0;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.sp-teaser-close svg { display: block; width: 9px; height: 9px; }

/* Side tab: a bare glyph riding in the tab's own flow, after the label. */
.sp-teaser-style-tab .sp-teaser-close {
  opacity: 0.6;
  transition: opacity 0.15s ease;
}
.sp-teaser-style-tab .sp-teaser-close:hover,
.sp-teaser-style-tab .sp-teaser-close:focus-visible { opacity: 1; }
.sp-teaser-style-tab .sp-teaser-close svg { width: 14px; height: 14px; }

/* Floating looks: a chip pinned to the TOP-LEFT corner — the top-right is
   where the attention dot/badge lives. Filled with the launcher's own
   background/text pair, so it stays legible on any color the merchant (or
   the synced theme) lands on. */
.sp-teaser-float .sp-teaser-close {
  position: absolute;
  top: -8px;
  left: -8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--sp-teaser-bg);
  color: var(--sp-teaser-text);
  border: 2px solid var(--sp-teaser-attn-border);
  box-shadow: 0 2px 6px rgba(14, 34, 24, 0.35);
  z-index: 4;
}

/* ==========================================================================
   Look: Side tab (default) — solid vertical/horizontal edge tab. Icon sits at
   the top, label reads bottom-to-top. No border; a colored drop shadow lifts
   it off the page (matches the design's tab spec: 22/13 padding, 18px radius).
   ========================================================================== */
.sp-teaser-style-tab {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 22px 13px;
  background: var(--sp-teaser-bg);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  text-align: center;
  writing-mode: vertical-rl;
  max-height: 310px;
  /* A tight shadow that projects inward off the edge the tab hugs — not the
     floating looks' big blooming shadow, half of which clips against the page
     edge while the rest smears a heavy dark halo across the page. Direction is
     set per placement below (default here is the right edge: shadow to the
     left). Mirrors the tabShadow in Teaser.jsx. */
  box-shadow: -6px 8px 22px -12px var(--sp-teaser-shadow);
}
.sp-teaser-style-tab .sp-teaser-label { transform: rotate(180deg); }
.sp-teaser-style-tab.sp-placement-left { box-shadow: 6px 8px 22px -12px var(--sp-teaser-shadow); }
.sp-teaser-style-tab.sp-placement-right-bottom,
.sp-teaser-style-tab.sp-placement-left-bottom { box-shadow: 0 -6px 22px -12px var(--sp-teaser-shadow); }

.sp-teaser-style-tab.sp-placement-right,
.sp-teaser-style-tab.sp-placement-right-bottom {
  right: 0;
  border-radius: 18px 0 0 18px;
}
.sp-teaser-style-tab.sp-placement-left,
.sp-teaser-style-tab.sp-placement-left-bottom {
  left: 0;
  border-radius: 0 18px 18px 0;
}
.sp-teaser-style-tab.sp-placement-left .sp-teaser-label,
.sp-teaser-style-tab.sp-placement-left-bottom .sp-teaser-label { transform: rotate(0deg); }

.sp-teaser-style-tab.sp-placement-right,
.sp-teaser-style-tab.sp-placement-left {
  top: 50%;
  transform: translateY(-50%) translateX(0);
}
.sp-teaser-style-tab.sp-placement-right:not(.sp-show) { transform: translateY(-50%) translateX(110%); }
.sp-teaser-style-tab.sp-placement-left:not(.sp-show) { transform: translateY(-50%) translateX(-110%); }

.sp-teaser-style-tab.sp-placement-right-bottom,
.sp-teaser-style-tab.sp-placement-left-bottom {
  writing-mode: horizontal-tb;
  top: auto;
  bottom: 0;
  padding: 15px 24px;
  max-width: 310px;
  border-radius: 18px 18px 0 0;
  transform: translateY(0);
}
.sp-teaser-style-tab.sp-placement-right-bottom { right: 24px; left: auto; }
.sp-teaser-style-tab.sp-placement-left-bottom { left: 24px; right: auto; }
.sp-teaser-style-tab.sp-placement-right-bottom .sp-teaser-label,
.sp-teaser-style-tab.sp-placement-left-bottom .sp-teaser-label { transform: none; }
.sp-teaser-style-tab.sp-placement-right-bottom:not(.sp-show),
.sp-teaser-style-tab.sp-placement-left-bottom:not(.sp-show) { transform: translateY(110%); }

.sp-teaser-style-tab.sp-placement-right.sp-show:hover { padding-right: 24px; }
.sp-teaser-style-tab.sp-placement-left.sp-show:hover { padding-left: 24px; }
.sp-teaser-style-tab.sp-placement-right-bottom.sp-show:hover,
.sp-teaser-style-tab.sp-placement-left-bottom.sp-show:hover { padding-bottom: 22px; }

/* ==========================================================================
   Floating looks (Clean pill / Bold bubble) share a body wrapper.
   The button positions; the body is the visible, animated launcher.
   ========================================================================== */
.sp-teaser-float { display: inline-flex; }
.sp-teaser-float .sp-teaser-body {
  display: inline-flex;
  align-items: center;
  position: relative;
  box-sizing: border-box;
}

/* Attention cue pinned to the top-right corner of the body. */
.sp-teaser-float .sp-teaser-attn {
  position: absolute;
  top: -9px;
  right: -9px;
  z-index: 3;
  pointer-events: none;
}
.sp-teaser-attn .sp-teaser-dot {
  position: relative;
  display: block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--sp-teaser-attn);
  border: 2px solid var(--sp-teaser-attn-border);
  box-shadow: 0 1px 3px rgba(14, 34, 24, 0.4);
}
.sp-teaser-attn .sp-teaser-ping {
  position: absolute;
  inset: 0;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--sp-teaser-attn);
  animation: sp-teaser-ping 1.8s ease-out infinite;
}

/* ---- Clean pill: horizontal floating pill with an icon ---- */
.sp-teaser-style-pill .sp-teaser-body {
  gap: 11px;
  height: 54px;
  padding: 0 19px;
  border-radius: 999px;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: 0.1px;
  white-space: nowrap;
  max-width: 280px;
  animation: sp-teaser-peek 5.5s ease-in-out infinite;
}
.sp-teaser-style-pill .sp-teaser-label { overflow: hidden; text-overflow: ellipsis; }

/* Solid fill, no border, drop shadow tinted by the background color. */
.sp-teaser-style-pill .sp-teaser-body {
  background: var(--sp-teaser-bg);
  box-shadow: 0 12px 30px -10px var(--sp-teaser-shadow), 0 3px 8px -4px rgba(14, 34, 24, 0.4);
}

/* Clean pill draws attention with the pinging dot (the glass surface stays
   quiet — see the effect section below). */
.sp-teaser-style-pill .sp-teaser-attn { display: block; width: 13px; height: 13px; }

/* ---- Bold bubble: a large gradient circle that gently pulses, topped with a
   small "Win" badge. ---- */
.sp-teaser-style-bubble .sp-teaser-body {
  justify-content: center;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--sp-teaser-grad-a), var(--sp-teaser-grad-b));
  box-shadow: 0 12px 30px -10px var(--sp-teaser-shadow), 0 3px 8px -4px rgba(14, 34, 24, 0.4);
  animation: sp-teaser-pulse 2.2s ease-in-out infinite;
}
.sp-teaser-style-bubble .sp-teaser-icon svg { width: 26px; height: 26px; }
.sp-teaser-style-bubble .sp-teaser-attn { display: block; }
.sp-teaser-style-bubble .sp-teaser-ping,
.sp-teaser-style-bubble .sp-teaser-dot { display: none; }
.sp-teaser-style-bubble .sp-teaser-badge {
  display: inline-flex;
  align-items: center;
  height: 21px;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--sp-teaser-badge-bg);
  color: var(--sp-teaser-badge-text);
  font-weight: 700;
  font-size: 10.5px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  border: 2px solid var(--sp-teaser-attn-border);
  box-shadow: 0 2px 6px rgba(14, 34, 24, 0.35);
}
/* Label kept for screen readers but visually hidden inside the circle. */
.sp-teaser-style-bubble .sp-teaser-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Effect: Glass — a frosted, translucent surface that ANY of the three looks
   can wear (sp-teaser-glass, set per campaign in JS). It used to be a fourth
   look, "Soft glass", which was exactly the Clean pill wearing this; a record
   still carrying that style resolves to the pair (see resolveStyle in
   app/lib/teaser.js), so those teasers are unchanged.

   Every tint comes from the same --sp-teaser-glass-* custom properties the
   server already derives from the launcher background, so the effect costs
   the payload nothing new. Mirrors the glass branches in the admin's
   Launcher.jsx.
   ========================================================================== */

/* Floating looks: the visible body is the pill / circle, so it takes the
   surface. Two classes here outrank the per-look fills above. */
.sp-teaser-glass.sp-teaser-float .sp-teaser-body {
  background: linear-gradient(135deg, var(--sp-teaser-glass-a), var(--sp-teaser-glass-b));
  border: 1px solid var(--sp-teaser-glass-border);
  -webkit-backdrop-filter: blur(12px) saturate(1.5);
  backdrop-filter: blur(12px) saturate(1.5);
  box-shadow: 0 10px 30px -10px var(--sp-teaser-glass-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

/* Side tab: the tab draws its own fill on the root (its body is
   display:contents), so the surface goes there. Deliberately no box-shadow —
   the per-placement edge shadows above are the same specificity and would be
   clobbered, and a frosted tab wants that tight inward shadow just as much as
   a solid one does. */
.sp-teaser-glass.sp-teaser-style-tab {
  background: linear-gradient(135deg, var(--sp-teaser-glass-a), var(--sp-teaser-glass-b));
  border: 1px solid var(--sp-teaser-glass-border);
  -webkit-backdrop-filter: blur(12px) saturate(1.5);
  backdrop-filter: blur(12px) saturate(1.5);
}

/* The glass surface is there to blend into the page, so it drops the pill's
   pinging dot. That is also what keeps a migrated Soft glass teaser looking
   exactly as it did — it has never shown one. The bubble's badge carries the
   offer's words rather than just noise, so it stays. */
.sp-teaser-glass.sp-teaser-style-pill .sp-teaser-attn { display: none; }

/* The dismiss chip wears the surface too, so it doesn't read as a solid blob
   stuck to a frosted launcher. Lighter shadow than the solid chip: most of the
   chip sits off the launcher, over the page, where a heavy shadow would muddy
   the blur. (The side tab's × is a bare glyph on the tab itself — it inherits
   the frosted surface for free.) */
.sp-teaser-glass.sp-teaser-float .sp-teaser-close {
  background: linear-gradient(135deg, var(--sp-teaser-glass-a), var(--sp-teaser-glass-b));
  border: 1px solid var(--sp-teaser-glass-border);
  -webkit-backdrop-filter: blur(12px) saturate(1.5);
  backdrop-filter: blur(12px) saturate(1.5);
  box-shadow: 0 2px 6px rgba(14, 34, 24, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

/* The bubble's badge rides on the frosted launcher, so it wears the surface
   too — a solid amber chip on a glass bubble reads as a sticker. It keeps the
   2px border width the solid badge has (only the color changes), so the badge
   is exactly the same size either way; and it takes the launcher's own text
   color, since --sp-teaser-badge-text is picked to contrast with the solid
   badge fill that is no longer there. */
.sp-teaser-glass.sp-teaser-style-bubble .sp-teaser-badge {
  background: linear-gradient(135deg, var(--sp-teaser-glass-a), var(--sp-teaser-glass-b));
  border-color: var(--sp-teaser-glass-border);
  color: var(--sp-teaser-text);
  -webkit-backdrop-filter: blur(12px) saturate(1.5);
  backdrop-filter: blur(12px) saturate(1.5);
  box-shadow: 0 2px 6px rgba(14, 34, 24, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

/* ---- Shared placement + slide-in for the floating looks ----
   Eight anchors, mirroring the admin's 3×3 position grid (the side tab keeps
   its own four-anchor rules above). */
.sp-teaser-float.sp-placement-right { right: 16px; top: 50%; transform: translateY(-50%); }
.sp-teaser-float.sp-placement-right:not(.sp-show) { transform: translateY(-50%) translateX(150%); }
.sp-teaser-float.sp-placement-left { left: 16px; top: 50%; transform: translateY(-50%); }
.sp-teaser-float.sp-placement-left:not(.sp-show) { transform: translateY(-50%) translateX(-150%); }
.sp-teaser-float.sp-placement-right-bottom { right: 16px; bottom: 16px; }
.sp-teaser-float.sp-placement-right-bottom:not(.sp-show) { transform: translateY(160%); }
.sp-teaser-float.sp-placement-left-bottom { left: 16px; bottom: 16px; }
.sp-teaser-float.sp-placement-left-bottom:not(.sp-show) { transform: translateY(160%); }
.sp-teaser-float.sp-placement-center-bottom { left: 50%; bottom: 16px; transform: translateX(-50%); }
.sp-teaser-float.sp-placement-center-bottom:not(.sp-show) { transform: translateX(-50%) translateY(160%); }
.sp-teaser-float.sp-placement-right-top { right: 16px; top: 16px; }
.sp-teaser-float.sp-placement-right-top:not(.sp-show) { transform: translateY(-160%); }
.sp-teaser-float.sp-placement-left-top { left: 16px; top: 16px; }
.sp-teaser-float.sp-placement-left-top:not(.sp-show) { transform: translateY(-160%); }

/* ---- Size variants (sp-teaser-size-*) ----
   Medium is the base styling above; small/large scale each look's dimensions,
   type and icon. Set per campaign in JS. Mirrors SIZE_METRICS in the admin
   Reminder component so the preview matches. These sit after the look rules so
   the modifiers win the cascade at equal-or-higher specificity. */

/* Icon on the tab/pill/glass looks (the bubble sizes its own icon below). */
.sp-teaser-size-small .sp-teaser-icon svg { width: 18px; height: 18px; }
.sp-teaser-size-large .sp-teaser-icon svg { width: 26px; height: 26px; }

/* Side tab: vertical edge tab, and its horizontal bottom-corner variant. */
.sp-teaser-style-tab.sp-teaser-size-small { padding: 16px 10px; font-size: 11px; letter-spacing: 1.2px; max-height: 260px; }
.sp-teaser-style-tab.sp-teaser-size-large { padding: 28px 16px; font-size: 14px; max-height: 360px; }
.sp-teaser-style-tab.sp-teaser-size-small.sp-placement-right-bottom,
.sp-teaser-style-tab.sp-teaser-size-small.sp-placement-left-bottom { padding: 11px 18px; max-width: 260px; }
.sp-teaser-style-tab.sp-teaser-size-large.sp-placement-right-bottom,
.sp-teaser-style-tab.sp-teaser-size-large.sp-placement-left-bottom { padding: 19px 30px; max-width: 360px; }

/* Clean pill: height, padding and type scale together. */
.sp-teaser-size-small.sp-teaser-style-pill .sp-teaser-body { gap: 9px; height: 44px; padding: 0 15px; font-size: 12.5px; max-width: 240px; }
.sp-teaser-size-large.sp-teaser-style-pill .sp-teaser-body { gap: 13px; height: 64px; padding: 0 24px; font-size: 16.5px; max-width: 320px; }

/* Bold bubble: the circle and its icon (overrides the generic icon rule). */
.sp-teaser-size-small.sp-teaser-style-bubble .sp-teaser-body { width: 64px; height: 64px; }
.sp-teaser-size-small.sp-teaser-style-bubble .sp-teaser-icon svg { width: 21px; height: 21px; }
.sp-teaser-size-large.sp-teaser-style-bubble .sp-teaser-body { width: 96px; height: 96px; }
.sp-teaser-size-large.sp-teaser-style-bubble .sp-teaser-icon svg { width: 31px; height: 31px; }

/* Dismiss control: the tab's bare glyph and the floating looks' chip both
   scale with the launcher. Mirrors SIZE_METRICS.close in Launcher.jsx. */
.sp-teaser-size-small.sp-teaser-style-tab .sp-teaser-close svg { width: 12px; height: 12px; }
.sp-teaser-size-large.sp-teaser-style-tab .sp-teaser-close svg { width: 16px; height: 16px; }
.sp-teaser-size-small.sp-teaser-float .sp-teaser-close { width: 19px; height: 19px; }
.sp-teaser-size-small.sp-teaser-float .sp-teaser-close svg { width: 8px; height: 8px; }
.sp-teaser-size-large.sp-teaser-float .sp-teaser-close { width: 25px; height: 25px; }
.sp-teaser-size-large.sp-teaser-float .sp-teaser-close svg { width: 10px; height: 10px; }
.sp-teaser-float.sp-placement-center-top { left: 50%; top: 16px; transform: translateX(-50%); }
.sp-teaser-float.sp-placement-center-top:not(.sp-show) { transform: translateX(-50%) translateY(-160%); }

@media (max-width: 600px) {
  .sp-teaser-style-tab {
    padding: 16px 10px;
    font-size: 11px;
    letter-spacing: 1.2px;
  }
}

/* ===========================================================================
   Countdown timer
   Four styles driven by config: line / pill / ticker (inline, under the code)
   and topbar (a hazard bar on the popup chrome). Accent colour is injected as
   --sp-timer-accent* custom properties by JS. Mirrors TimerPreview.jsx.
   =========================================================================== */

@keyframes sp-timer-dot {
  0%, 100% { opacity: 0.3; transform: scale(0.7); }
  50%      { opacity: 1;   transform: scale(1); }
}
@keyframes sp-timer-stripes {
  from { background-position: 0 0; }
  to   { background-position: 40px 0; }
}

/* ---- Topbar style ---- */
.sp-has-topbar .sp-body { padding-top: 60px; }
/* Push the X clear of the bar — but only when it's anchored to the top: the
   bottom corners set `top: auto`, so they simply ignore this inset. (The colour
   is legacy: #scratch-popup .sp-close wins with !important, so the X keeps the
   merchant's --sp-close-color either way.) */
.sp-has-topbar .sp-close { color: #ffffff; --sp-close-top: 42px; }

.sp-timer-topbar {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 34px;
  z-index: 5;
  color: var(--sp-timer-text, #ffffff);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-family: var(--sp-font, 'Inter', system-ui, sans-serif);
  font-size: calc(11px * var(--sp-timer-scale, 1));
  font-weight: 600;
  line-height: 1;
}
.sp-timer-topbar b {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 800;
  font-size: calc(12.5px * var(--sp-timer-scale, 1));
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}
.sp-timer-tb-text {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.sp-timer-tb-dot {
  position: relative;
  background: var(--sp-timer-text, #ffffff);
}
.sp-timer-stripes {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image: repeating-linear-gradient(45deg, transparent 0 9px, rgba(255, 255, 255, 0.1) 9px 18px);
  animation: sp-timer-stripes 0.9s linear infinite;
}

/* ---- Shared inline atoms ---- */
.sp-timer-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
  animation: sp-timer-dot 1.1s ease-in-out infinite;
}
.sp-timer-clock-glyph { flex-shrink: 0; }

.sp-timer-inline {
  width: 100%;
  margin-top: 12px;
  font-family: var(--sp-font, 'Inter', system-ui, sans-serif);
  /* Pin to a tight line box so the storefront theme's body line-height (1.4–1.5)
     can't leak in and inflate the inline timers. Mirrors the preview's `/1`
     (TimerPreview.jsx) and the topbar rule above. Unitless 1 cascades so each
     child's line box equals its own font-size. */
  line-height: 1;
  /* Centers the inline-flex line/pill children; the block-level ticker fills
     100% width and is unaffected. The wrapper itself carries no background. */
  text-align: center;
}
/* The top slot sits above the eyebrow/headline on every screen, so it needs a
   clear gap below it to read as the offer's lead line. Targeted by id because
   renderInlineTimer rewrites the element's class on each tick. */
#sp-timer-top { margin-bottom: 18px; }
.sp-timer-inline b {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 700;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--sp-timer-text, var(--sp-timer-accent));
}

/* ---- line ---- */
.sp-timer-line {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: calc(11.5px * var(--sp-timer-scale, 1));
  font-weight: 500;
  color: var(--sp-timer-text, var(--sp-footer-color, #a9a39a));
}
.sp-timer-line .sp-timer-dot {
  background: var(--sp-timer-accent);
  animation-duration: 1.6s;
}

/* ---- pill ---- */
.sp-timer-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: calc(8px * var(--sp-timer-scale, 1)) calc(16px * var(--sp-timer-scale, 1)) calc(8px * var(--sp-timer-scale, 1)) calc(14px * var(--sp-timer-scale, 1));
  border-radius: 999px;
  background: var(--sp-timer-accent-weak);
  color: var(--sp-timer-text, var(--sp-timer-accent));
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: calc(13.5px * var(--sp-timer-scale, 1));
  font-weight: 700;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
}

/* ---- ticker ---- */
.sp-timer-ticker {
  position: relative;
  overflow: hidden;
  border-radius: 10px;
  padding: calc(9px * var(--sp-timer-scale, 1)) calc(12px * var(--sp-timer-scale, 1));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: var(--sp-timer-accent-weak);
}
.sp-timer-ticker .sp-timer-stripes {
  opacity: 0.6;
  background-image: repeating-linear-gradient(45deg, transparent 0 10px, var(--sp-timer-accent-faint) 10px 20px);
}
.sp-timer-tk-text {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: calc(11px * var(--sp-timer-scale, 1));
  font-weight: 700;
  color: var(--sp-timer-text, var(--sp-timer-accent));
}
.sp-timer-tk-text .sp-timer-dot {
  width: 7px;
  height: 7px;
  background: var(--sp-timer-accent);
  animation-duration: 1.1s;
}
.sp-timer-tk-clock {
  position: relative;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: calc(14px * var(--sp-timer-scale, 1));
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--sp-timer-text, var(--sp-timer-accent));
}

/* ---- digital (bold segmented countdown) ---- */
.sp-timer-digital {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(9px * var(--sp-timer-scale, 1));
  width: 100%;
}
.sp-timer-dg-heading {
  font-size: calc(11px * var(--sp-timer-scale, 1));
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sp-timer-text, var(--sp-timer-accent));
}
.sp-timer-dg-clock {
  display: flex;
  align-items: flex-start;
  gap: calc(6px * var(--sp-timer-scale, 1));
}
.sp-timer-dg-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(3px * var(--sp-timer-scale, 1));
  padding: calc(7px * var(--sp-timer-scale, 1)) calc(9px * var(--sp-timer-scale, 1));
  min-width: calc(34px * var(--sp-timer-scale, 1));
  border-radius: calc(9px * var(--sp-timer-scale, 1));
  background: var(--sp-timer-accent-weak);
}
.sp-timer-dg-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 800;
  font-size: calc(24px * var(--sp-timer-scale, 1));
  line-height: 1;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--sp-timer-text, var(--sp-timer-accent));
}
.sp-timer-dg-lbl {
  font-size: calc(8.5px * var(--sp-timer-scale, 1));
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0.7;
  color: var(--sp-timer-text, var(--sp-timer-accent));
}
.sp-timer-dg-colon {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 800;
  font-size: calc(20px * var(--sp-timer-scale, 1));
  line-height: 1;
  color: var(--sp-timer-accent);
  margin-top: calc(8px * var(--sp-timer-scale, 1));
}
.sp-timer-dg-expired {
  font-size: calc(12px * var(--sp-timer-scale, 1));
  font-weight: 500;
  color: var(--sp-footer-color, #a9a39a);
}
/* crit pulse on the whole clock (mirrors the other inline styles) */
.sp-timer-crit .sp-timer-dg-clock {
  animation: sp-timer-pulse 0.85s ease-in-out infinite;
  transform-origin: center;
}
@media (prefers-reduced-motion: reduce) {
  .sp-timer-crit .sp-timer-dg-clock { animation: none; }
}

/* ---- Expired state (inline) ---- */
.sp-timer-expired .sp-timer-line,
.sp-timer-expired .sp-timer-pill,
.sp-timer-expired .sp-timer-tk-text,
.sp-timer-expired .sp-timer-tk-clock { color: var(--sp-footer-color, #a9a39a); }
.sp-timer-expired .sp-timer-pill,
.sp-timer-expired .sp-timer-ticker { background: var(--sp-timer-accent-faint); }
.sp-timer-expired .sp-timer-dot { background: var(--sp-footer-color, #a9a39a); animation: none; }
.sp-timer-expired b { color: var(--sp-footer-color, #a9a39a) !important; }

/* ---- Size (S / M / L) ---- */
/* A single multiplier the font-size/padding rules above read. Applied as a class
   on the inline box / topbar by the storefront + mirrored in the admin preview. */
.sp-timer-size-small  { --sp-timer-scale: 0.86; }
.sp-timer-size-medium { --sp-timer-scale: 1; }
.sp-timer-size-large  { --sp-timer-scale: 1.26; }

/* ---- Urgency escalation ---- */
/* The accent is swapped to amber ("warn") then red ("crit") by JS. Here we add
   the motion: a subtle pulse on the inline chips and a brightness flash on the
   topbar in the final stretch, so the countdown visibly ramps up. Reduced-motion
   users keep the colour change without the animation. */
@keyframes sp-timer-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.05); }
}
@keyframes sp-timer-flash {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.16); }
}
.sp-timer-crit .sp-timer-line,
.sp-timer-crit .sp-timer-pill,
.sp-timer-crit .sp-timer-ticker {
  animation: sp-timer-pulse 0.85s ease-in-out infinite;
  transform-origin: center;
}
.sp-timer-topbar.sp-timer-crit {
  animation: sp-timer-flash 0.85s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .sp-timer-crit .sp-timer-line,
  .sp-timer-crit .sp-timer-pill,
  .sp-timer-crit .sp-timer-ticker,
  .sp-timer-topbar.sp-timer-crit { animation: none; }
}
