/*
 * Enter any custom CSS here.
 * This file will not be overwritten by theme updates.
 *
 * ── Semantic class hooks (sa-*) ────────────────────────────────────────────
 * Every section, snippet and key element carries a stable `sa-` class you can
 * target with plain CSS — no Tailwind rebuild needed. These classes ship with
 * NO styles of their own; they exist purely as selectors for you to hook onto.
 *
 * Page scope   body.sa-tpl-<template>      e.g. .sa-tpl-product, .sa-tpl-shop
 * Chrome       .sa-header .sa-navbar .sa-footer
 *              .sa-navbar-link .sa-navbar-logo .sa-navbar-cart .sa-footer-link
 * Sections     .sa-<component>             e.g. .sa-hero, .sa-features, .sa-faq
 *   variant    .sa-<component>--<variant>  e.g. .sa-hero--split-media
 *   inner      .sa-<component>-title / -subtitle / -eyebrow / -media /
 *              -items / -item / -actions / -link / -price / -badge / -icon
 * Primitives   .sa-btn (+ .sa-btn--<variant>), .sa-section-header
 *              (.sa-section-title / -eyebrow / -subtitle),
 *              .sa-product-card (+ .sa-product-card--<style>,
 *              .sa-product-card-title / -price / -stock / -status / -footer),
 *              .sa-product-form + .sa-add-to-cart, .sa-product-gallery,
 *              .sa-cart-panel + .sa-cart-item, .sa-searchbar, .sa-pagination,
 *              .sa-breadcrumbs, .sa-toasts + .sa-toast, .sa-feedback-card
 *
 * Example — bigger hero heading only on the product page:
 *   .sa-tpl-product .sa-hero-title { font-size: 3rem; }
 * ───────────────────────────────────────────────────────────────────────────
*/

/* ==========================================================================
   FALLENHUETA — custom theme layer
   Design tokens (colours, fonts, radii, shadows, spacing) live in
   settings.json — this file only adds things the theme customiser has no
   control for: the signature HUD-bracket motif, the hero glitch word, the
   pill header, the mono data face, and repositioning the built-in "coupon"
   popup into a quiet Discord toast. Organised by area, top to bottom.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Extra tokens
   settings.json only exposes one accent colour ("Signal"). We add one more
   — "Tracer", a colder blue — used ONLY by the signature bracket motif, so
   it keeps a specific meaning instead of becoming a second background hue.
   A monospace face is added for anything that is literally data: price,
   stock count, order/variant codes.
   -------------------------------------------------------------------------- */

@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  --cl-tracer: 46 134 255;      /* #2E86FF — signature-motif blue, not used in backgrounds/gradients */
  --ff-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  --ease-fh: cubic-bezier(.22, 1, .36, 1);       /* smooth, snappy settle — most transitions */
  --ease-fh-spring: cubic-bezier(.34, 1.56, .64, 1); /* slight overshoot — nav pill, playful moments */
}

/* .fh-mono stays available as a utility, but is no longer force-applied to
   price/stock: on narrow 4-up cards a monospace price range ($X.XX–$X.XX
   plus a struck-through range) was wide enough to squeeze the stock text
   next to it down to almost nothing, wrapping it letter-by-letter into
   the price — the "crooked prices" bug. Reverting to the body font fixes
   it outright; the flex-wrap below is a second safety net so the same
   row can never collide again even with long content. */
.fh-mono {
  font-family: var(--ff-mono);
  letter-spacing: -0.01em;
}
.sa-product-card--flat-divider .p-4 > .flex.items-end.justify-between {
  flex-wrap: wrap;
  row-gap: 0.25rem;
}
.sa-product-card--flat-divider .p-4 > .flex.items-end.justify-between > * {
  min-width: 0;
}

/* Same story, better fix: tabular-nums instead of a font swap.
   Forcing .fh-mono onto price/stock was the ORIGINAL fix for uneven
   digit widths, and it's exactly what caused the wrap bug above — a
   full monospace face is meaningfully wider per character than the
   body font, which is what squeezed the stock text into nothing on
   narrow cards. `font-variant-numeric: tabular-nums` gets the same
   benefit (digits line up, a price never visually "hops" width when
   it changes between variants) without switching the typeface or its
   character width, so it can't reintroduce that bug. */
.sa-product-card-price,
.sa-product-card-stock {
  font-variant-numeric: tabular-nums;
}

/* Belt-and-braces guard against horizontal overflow — if any element ever
   ends up a hair wider than the viewport (a vw-based width, a fixed-width
   badge, whatever), this stops the whole page from picking up a sideways
   scrollbar, which is what usually reads as the layout looking "crooked"
   on a phone. `clip` (not `hidden`) so it doesn't break `position: sticky`
   elements like the nav. */
html {
  overflow-x: clip;
}

/* Product cards — and now feature cards too — get the same quiet bracket
   reveal on hover/focus: no cursor-tracking glow, no tilt, just the frame
   switching on. Kept as one shared rule so the two card types read as
   the same interactive language rather than two different treatments. */
.sa-product-card,
.sa-features--icon-grid .sa-features-item {
  position: relative;
  transition: border-color 220ms var(--ease-fh), box-shadow 220ms var(--ease-fh), transform 220ms var(--ease-fh);
}
.sa-product-card::after,
.sa-features--icon-grid .sa-features-item::after {
  content: '';
  position: absolute;
  inset: 10px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 220ms var(--ease-fh);
  background-repeat: no-repeat;
  background-image:
    linear-gradient(to right, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to bottom, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to left, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to bottom, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to left, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to top, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to right, rgb(var(--cl-tracer)) 2px, transparent 2px),
    linear-gradient(to top, rgb(var(--cl-tracer)) 2px, transparent 2px);
  background-size:
    16px 2px, 2px 16px,
    16px 2px, 2px 16px,
    16px 2px, 2px 16px,
    16px 2px, 2px 16px;
  background-position:
    top left, top left,
    top right, top right,
    bottom right, bottom right,
    bottom left, bottom left;
}
.sa-product-card:hover,
.sa-product-card:focus-visible {
  border-color: rgba(var(--card-accent, var(--cl-accent)), 0.45);
  transform: translateY(-2px);
}
.sa-features--icon-grid .sa-features-item:hover,
.sa-features--icon-grid .sa-features-item:focus-visible {
  border-color: rgba(var(--cl-accent), 0.45);
  box-shadow: 0 18px 40px -18px rgba(var(--cl-accent), 0.3), 0 0 60px -12px rgba(var(--cl-accent), 0.22);
  transform: translateY(-2px);
}
.sa-product-card:hover::after,
.sa-product-card:focus-visible::after,
.sa-features--icon-grid .sa-features-item:hover::after,
.sa-features--icon-grid .sa-features-item:focus-visible::after {
  opacity: 1;
}
.sa-features--icon-grid .sa-features-item .size-10 {
  transition: box-shadow 220ms var(--ease-fh);
}
.sa-features--icon-grid .sa-features-item:hover .size-10,
.sa-features--icon-grid .sa-features-item:focus-visible .size-10 {
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.3), 0 0 16px rgba(var(--cl-accent), 0.28), 0 0 38px rgba(var(--cl-accent), 0.16);
}
@media (prefers-reduced-motion: reduce) {
  .sa-product-card,
  .sa-product-card::after,
  .sa-features--icon-grid .sa-features-item,
  .sa-features--icon-grid .sa-features-item::after {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   2. Hero — typewriter reveal + the glitch effect ported from your old
   theme's stylesheet (theme-257041.zip, "HERO TYPEWRITER + GLITCH" block).
   custom.js splits the title into one <span class="fh-tw-char"> per
   character and reveals them in sequence; regular characters get a
   simple fade-up, the highlighted word gets the ported glitch keyframe
   instead. The glitch's red/cyan/magenta flashes are kept as in the
   original — that's the actual visual grammar of a channel-split glitch,
   not a change to the site's colour palette, which stays untouched
   everywhere else.
   -------------------------------------------------------------------------- */

/* Stats sit directly under the hero as one visual unit, like the old
   design — remove the standard section gap between just this pair. */
.sa-hero + .sa-stats {
  padding-top: 0;
}

.fh-tw-char {
  display: inline-block;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 230ms var(--ease-fh), transform 230ms var(--ease-fh);
}
.fh-tw-char.visible {
  opacity: 1;
  transform: translateY(0);
}
.fh-tw-char.fh-tw-hl {
  color: rgb(var(--cl-accent));
}

.fh-tw-caret {
  display: inline-block;
  width: 3px;
  height: 0.85em;
  margin-left: 0.08em;
  vertical-align: -0.1em;
  border-radius: 2px;
  background: rgb(var(--cl-accent));
  box-shadow: 0 0 6px rgba(var(--cl-accent), 0.6), 0 0 20px rgba(var(--cl-accent), 0.32), 0 0 40px rgba(var(--cl-accent), 0.16);
  animation: fh-tw-blink 1.05s ease-in-out infinite;
}
.fh-tw-caret.fh-tw-caret-done {
  animation: none;
  opacity: 0;
  transition: opacity 400ms var(--ease-fh);
}
@keyframes fh-tw-blink {
  0%, 45%   { opacity: 1; }
  55%, 100% { opacity: 0; }
}

/* Ported glitch keyframe — same colours, skew/scale jitter and
   hue-rotate as the original cpGlitch, condensed from ~19 stops to ~13
   for maintainability, settling into the site's accent glow at 100%
   instead of holding the original's own blue. Turned up from the first
   pass — bigger displacement/skew and an extra stutter jolt at 60%. */
.fh-tw-char.fh-tw-hl.glitch {
  animation: fh-cp-glitch 860ms ease-out forwards;
}
@keyframes fh-cp-glitch {
  0%   { transform: translateX(-22px) skewX(52deg) scaleY(1.16) scaleX(.88); opacity: .2; color: #ff003c; text-shadow: 16px 0 #0ff, -16px 0 #f0f, 0 0 34px #ff003c; filter: blur(6px) brightness(3) hue-rotate(200deg); }
  4%   { transform: translateX(24px) skewX(-58deg) scaleY(.84) scaleX(1.12); opacity: .6; color: #ffe600; text-shadow: -14px 0 #f0f, 14px 0 #0ff, 0 0 44px #ffe600; filter: blur(3px) brightness(3.5) hue-rotate(100deg); }
  10%  { transform: translateX(18px) skewX(-46deg) scaleY(.87) scaleX(1.08); opacity: .75; color: #00ffea; text-shadow: -13px 0 #f0f, 13px 0 #0ff, 0 0 55px #00ffea; filter: blur(1.5px) brightness(4); }
  16%  { transform: translateX(13px) skewX(-32deg) scaleY(.90) scaleX(1.07); opacity: .9; color: #fff; text-shadow: -10px 0 #f0f, 10px 0 #0ff, 0 0 65px #fff; filter: blur(0) brightness(4.5); }
  22%  { transform: translateX(15px) skewX(-22deg) scaleY(.94); opacity: .8; color: #00ffea; text-shadow: -7px 0 #f0f, 7px 0 #0ff, 0 0 40px #00ffea; filter: blur(0) brightness(2.8); }
  30%  { transform: translateX(9px) skewX(-12deg) scaleY(.96); opacity: .95; color: #fff; text-shadow: -5px 0 #f0f, 5px 0 #0ff, 0 0 24px #fff; filter: blur(0) brightness(2.2); }
  40%  { transform: translateX(6px) skewX(-6deg); opacity: .85; color: #00ffea; text-shadow: -4px 0 #f0f, 4px 0 #0ff; filter: blur(0); }
  50%  { transform: translateX(2px) skewX(-2deg); opacity: 1; color: rgb(var(--cl-accent)); text-shadow: 0 0 22px rgba(var(--cl-accent), .95); }
  60%  { transform: translateX(-9px) skewX(14deg) scaleX(1.04); opacity: .7; color: #ff003c; text-shadow: -8px 0 #0ff, 8px 0 #f0f, 0 0 30px #ff003c; filter: brightness(2.6); }
  68%  { transform: translateX(3px) skewX(-3deg); opacity: 1; color: rgb(var(--cl-accent)); text-shadow: 0 0 16px rgba(var(--cl-accent), .8); }
  80%  { transform: translateX(1px); opacity: 1; color: rgb(var(--cl-accent)); text-shadow: 0 0 14px rgba(var(--cl-accent), .7); }
  100% { transform: translateX(0) skewX(0) scaleY(1) scaleX(1); opacity: 1; color: rgb(var(--cl-accent));
         text-shadow: 0 0 10px rgba(var(--cl-accent), .65), 0 0 26px rgba(var(--cl-tracer), .40);
         filter: blur(0) brightness(1) hue-rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .fh-tw-char {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .fh-tw-caret {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   3. Header — shrinks into a floating pill past a scroll threshold
   .is-pill is toggled on #navbar by custom.js. Uses the "classic" navbar
   markup as-is; nothing here needs a template change.

   Why the old version snapped instead of easing: a CSS transition can only
   interpolate between two real numeric values. `max-width: none → 1136px`
   and `margin-inline: 0 → auto` both start from a keyword, not a number,
   so the browser just flips them instantly partway through instead of
   easing — same for box-shadow when the two states don't have the same
   number of shadow layers. Fixed by keeping every animated property a
   real number in BOTH states, and centering via the flex parent
   (justify-content: center) instead of margin-inline: auto.
   -------------------------------------------------------------------------- */

#navbar {
  display: flex;
  justify-content: center;
  transition: padding-top 460ms var(--ease-fh-spring);
}
#navbar > div {
  width: 100%;
  border: 1px solid transparent;
  box-shadow: 0 1px 0 0 rgba(var(--cl-accent), 0.12), 0 0 0 0 rgba(0, 0, 0, 0);
  transition: width 460ms var(--ease-fh-spring), border-radius 460ms var(--ease-fh-spring),
              border-color 460ms var(--ease-fh-spring), box-shadow 460ms var(--ease-fh-spring),
              background-color 460ms var(--ease-fh-spring);
}
#navbar.is-pill {
  padding-top: 12px;
}
#navbar.is-pill > div {
  width: min(calc(100% - 64px), calc(var(--container-max) - 64px));
  border-color: rgba(var(--cl-accent), 0.22);
  border-radius: 9999px;
  background-color: rgba(var(--cl-surface), 0.75);
  box-shadow: 0 10px 34px -14px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(var(--cl-accent), 0.06);
}
#navbar.is-pill > div .wrap {
  height: 3.25rem;
  padding-inline: 1.5rem;
  transition: height 460ms var(--ease-fh-spring), padding-inline 460ms var(--ease-fh-spring);
}
@media (prefers-reduced-motion: reduce) {
  #navbar,
  #navbar > div {
    transition: none;
  }
}

/* Brand wordmark — the theme applies font-heading (Syne) but hardcodes
   font-semibold (600); you specifically asked for Syne ExtraBold (800)
   on the shop name, which is what makes it read as Syne at a glance
   instead of "some default bold sans". */
.sa-navbar-logo .font-heading {
  font-weight: 800;
  letter-spacing: -0.02em;
}

/* Nav links — same bg-tint hover the theme ships with by default just
   reads as very flat; tinting it with the accent colour instead of grey
   and giving the active page a real underline makes the header feel
   considered rather than a plain link list. */
.sa-navbar-link {
  position: relative;
}
.sa-navbar-link:hover {
  background-color: rgba(var(--cl-accent), 0.08) !important;
  color: rgb(var(--cl-accent)) !important;
}
.sa-navbar-link.text-t-primary::after {
  content: '';
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  bottom: 3px;
  height: 2px;
  border-radius: 2px;
  background: rgb(var(--cl-accent));
  box-shadow: 0 0 6px rgba(var(--cl-accent), 0.5), 0 0 16px rgba(var(--cl-accent), 0.25);
}

/* Currency selector: inherits the new palette automatically since it's
   built entirely from CSS-variable-backed utility classes. The one thing
   it's missing is a lit-up state while open — aria-expanded is bound
   reactively by Alpine, so plain CSS can read it. */
.currency-selector button[aria-haspopup='listbox'][aria-expanded='true'] {
  border-color: rgba(var(--cl-accent), 0.5);
  box-shadow: 0 0 0 3px rgba(var(--cl-accent), 0.12);
}


/* --------------------------------------------------------------------------
   4. Product page — segmented variant selector polish
   variant_selector is set to "pills" in settings.json; this just tightens
   the active state so it reads as a toggle rather than a button group.
   -------------------------------------------------------------------------- */

.sa-product-form .rounded-btn[class*='border'] {
  transition: background-color 160ms var(--ease-fh), border-color 160ms var(--ease-fh), color 160ms var(--ease-fh);
}

/* --------------------------------------------------------------------------
   5. Discord invite — the built-in "coupon" popup repurposed as a quiet
   corner toast instead of a centred, overlaid dialog. Timing, dismissal
   and persistence (show_after_seconds / forget_after_hours) are handled
   natively by the theme's coupon logic — only positioning changes here.
   -------------------------------------------------------------------------- */

.sa-coupon > div {
  display: block;
  min-height: 0;
  position: fixed;
  inset: auto 1.25rem 1.25rem auto;
  padding: 0;
  z-index: 60;
  max-width: 21rem;
}
.sa-coupon > div > div:first-child {
  /* the modal's dark backdrop — a corner toast shouldn't dim the page */
  opacity: 0;
  pointer-events: none;
}
.sa-coupon > div > div:nth-child(2) {
  position: static;
  width: 100%;
  text-align: left;
  padding: 1.25rem;
  border-left: 3px solid rgb(var(--cl-accent));
  background-color: rgba(var(--cl-surface-raised), 0.88);
  backdrop-filter: blur(16px);
}
@media (max-width: 480px) {
  .sa-coupon > div {
    inset: auto 0.75rem 0.75rem 0.75rem;
    max-width: none;
  }
}

/* --------------------------------------------------------------------------
   6. Ambient grain — a barely-visible texture over the whole page so the
   single background glow doesn't sit completely flat. Attached to body,
   so no wrapper element is required.
   -------------------------------------------------------------------------- */

body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

/* ==========================================================================
   7. Second pass — a few ideas pulled from the old site's stylesheet
   You asked me to look at the previous theme's custom.css and borrow from
   it. It leans much more maximal than what we agreed (looping hue-shifts,
   rainbow CRT glitch, nebula backgrounds, permanent glows on every card) —
   that's the exact "kitchen sink" look we deliberately moved away from
   last round, so I didn't bring those pieces back wholesale. What follows
   is the handful of ideas from it that are genuinely good AND still fit a
   restrained site: a real gap you had (footer socials had no styling at
   all), one motif worth reviving from your original brand screenshot (the
   glowing divider line), and two small realism touches. Nothing here
   loops or animates on its own — it only responds to hover, same
   discipline as the rest of the site.
   -------------------------------------------------------------------------- */

/* 7a. Footer socials — each platform gets its own brand-tinted hover glow
   instead of one generic grey icon state. This has actual meaning (you
   can tell platforms apart at a glance) rather than being decoration, so
   it earns a place even under the "less is more" rule. aria-label carries
   the platform name already (see footer.njk), so no template edit needed. */
.sa-footer a[aria-label] {
  position: relative;
  transition: color 200ms var(--ease-fh), background-color 200ms var(--ease-fh), box-shadow 200ms var(--ease-fh);
}
.sa-footer a[aria-label='Discord']:hover {
  color: #7289DA;
  box-shadow: 0 0 0 1px rgba(114, 137, 218, 0.25), 0 0 14px rgba(114, 137, 218, 0.28), 0 0 32px rgba(114, 137, 218, 0.16);
}
.sa-footer a[aria-label='YouTube']:hover {
  color: #FF4444;
  box-shadow: 0 0 0 1px rgba(255, 68, 68, 0.25), 0 0 14px rgba(255, 68, 68, 0.24), 0 0 32px rgba(255, 68, 68, 0.14);
}
.sa-footer a[aria-label='FunPay']:hover {
  color: rgb(var(--cl-accent));
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.3), 0 0 14px rgba(var(--cl-accent), 0.26), 0 0 32px rgba(var(--cl-accent), 0.16);
}

/* 7b. Section-title divider — this is the flanking glowing line from your
   original "— • Our Products • —" heading. Revived as the site's second
   recurring motif (brackets = product focus, this = section rhythm),
   used consistently on every centered section title rather than once.
   Static, no shimmer loop — the glow does the work, not motion. */
.sa-section-header.text-center .sa-section-title {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}
.sa-section-header.text-center .sa-section-title::before,
.sa-section-header.text-center .sa-section-title::after {
  content: '';
  flex: 1 1 32px;
  height: 2px;
  min-width: 20px;
  background: linear-gradient(90deg, transparent, rgba(var(--cl-accent), 0.55), transparent);
  box-shadow: 0 0 8px rgba(var(--cl-accent), 0.3), 0 0 22px rgba(var(--cl-accent), 0.18);
}

/* 7c. Product cards — a faint diagonal gradient instead of a flat fill,
   so the surface has a touch of depth without being a "glow everywhere"
   effect. Same base colour, just not perfectly flat. */
.sa-product-card {
  background-image: linear-gradient(165deg, rgba(var(--cl-surface-raised), 0.55) 0%, transparent 55%);
}

/* 7d. Glass surfaces — a single hairline of light along the top edge,
   the one detail that reliably reads as "glass" rather than "dark box
   with blur". Applied only to the two places we actually use glass:
   the pill header and the Discord toast. */
#navbar.is-pill > div,
.sa-coupon > div > div:nth-child(2) {
  position: relative;
}
#navbar.is-pill > div::before,
.sa-coupon > div > div:nth-child(2)::before {
  content: '';
  position: absolute;
  top: 0;
  left: 12%;
  right: 12%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.16), transparent);
  pointer-events: none;
}

/* ==========================================================================
   8. "How it works" — richer timeline + scroll-driven progress + hover cascade
   Numbers were plain grey circles on a 1px grey line — functionally fine,
   visually inert. Base pass: a dim gradient track, a glow ring on each
   number, and a hover state so the section responds to the cursor at all.

   custom.js (§7) tracks how far the section has scrolled through the
   viewport and writes it to --fh-steps-progress (0→1) on the track
   element; ::before reads that to fill the wire top-down as you scroll
   to each step. Nothing here needs the JS to know any CSS — it only
   ever writes one number.

   On top of that (replacing the small traveling dot that used to ride
   the fill edge): hovering the step list lights the numbered circles
   themselves in sequence, 1 → 2 → 3, each with a short stagger via
   nth-child animation-delay — a deliberate "power-on" cascade down the
   list rather than one dot sliding along the wire.
   -------------------------------------------------------------------------- */
.sa-steps--timeline .absolute.left-5.top-3.bottom-3 {
  --fh-steps-progress: 0;
  width: 2px;
  overflow: visible;
  background: linear-gradient(to bottom, transparent, rgba(var(--cl-accent), 0.16) 12%, rgba(var(--cl-accent), 0.16) 88%, transparent);
}
.sa-steps--timeline .absolute.left-5.top-3.bottom-3::before {
  content: '';
  position: absolute;
  inset: 0;
  transform: scaleY(var(--fh-steps-progress));
  transform-origin: top;
  background: linear-gradient(to bottom, rgba(var(--cl-accent), 0.95), rgba(var(--cl-accent), 0.6));
  box-shadow: 0 0 12px rgba(var(--cl-accent), 0.5), 0 0 32px rgba(var(--cl-accent), 0.26);
  transition: transform 120ms linear;
}
@media (prefers-reduced-motion: reduce) {
  .sa-steps--timeline .absolute.left-5.top-3.bottom-3::before {
    transition: none;
  }
}
.sa-steps-item > span:first-child {
  width: 2.75rem;
  height: 2.75rem;
  font-size: 1rem;
  border-color: rgba(var(--cl-accent), 0.35) !important;
  background: radial-gradient(circle at 35% 30%, rgba(var(--cl-accent), 0.20), rgba(var(--cl-surface-raised), 1) 70%) !important;
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.15), 0 0 14px rgba(var(--cl-accent), 0.22), 0 0 34px rgba(var(--cl-accent), 0.14);
  transition: box-shadow 220ms var(--ease-fh), transform 220ms var(--ease-fh);
}
.sa-steps-item:hover > span:first-child,
.sa-steps-item:focus-within > span:first-child {
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.45), 0 0 20px rgba(var(--cl-accent), 0.42), 0 0 46px rgba(var(--cl-accent), 0.26);
  transform: scale(1.06);
}
/* Hover the LIST (not just one item) to run the 1→2→3 cascade down every
   circle — hovering a single item still gets its own instant highlight
   via the :hover/:focus-within rule just above. */
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item > span:first-child {
  animation: fh-step-light 900ms var(--ease-fh) both;
}
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item:nth-child(1) > span:first-child { animation-delay: 0ms; }
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item:nth-child(2) > span:first-child { animation-delay: 200ms; }
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item:nth-child(3) > span:first-child { animation-delay: 400ms; }
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item:nth-child(4) > span:first-child { animation-delay: 600ms; }
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item:nth-child(5) > span:first-child { animation-delay: 800ms; }
.sa-steps--timeline .sa-steps-items:hover .sa-steps-item:nth-child(6) > span:first-child { animation-delay: 1000ms; }
@keyframes fh-step-light {
  0%, 100% {
    box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.15), 0 0 14px rgba(var(--cl-accent), 0.22), 0 0 34px rgba(var(--cl-accent), 0.14);
    transform: scale(1);
  }
  45% {
    box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.6), 0 0 22px rgba(var(--cl-accent), 0.55), 0 0 52px rgba(var(--cl-accent), 0.34);
    transform: scale(1.1);
  }
}
.sa-steps-item h3 {
  transition: color 200ms var(--ease-fh);
}
.sa-steps-item:hover h3,
.sa-steps-item:focus-within h3 {
  color: rgb(var(--cl-accent));
}
@media (prefers-reduced-motion: reduce) {
  .sa-steps-item > span:first-child,
  .sa-steps-item h3 {
    transition: none;
  }
  .sa-steps--timeline .sa-steps-items:hover .sa-steps-item > span:first-child {
    animation: none;
  }
}

/* ==========================================================================
   9. Reviews — small polish on top of the built-in "glow" card style
   card_style is set to "glow" in settings.json, which already gives each
   card the corner blob + bottom hairline instead of a flat box. These are
   the two details that style doesn't cover: the verified badge and the
   slider controls, both of which were still the plain theme default.
   -------------------------------------------------------------------------- */
.sa-feedback-card span[title='From a completed order'] {
  padding: 0.15rem 0.55rem;
  border-radius: 9999px;
  background-color: rgba(var(--cl-success), 0.14);
}
.sa-feedbacks--slider .splide {
  padding-bottom: 0.5rem;
}
.sa-feedbacks--slider .splide__pagination {
  position: static;
  margin-top: 1.25rem;
  padding: 0;
}
/* Splide's own CSS hides pagination entirely (display: none) whenever the
   slide count doesn't exceed what's visible at once — e.g. exactly 3
   reviews at perPage: 3 on desktop. That rule (.splide:not(.is-overflow)
   .splide__pagination) is 3 classes deep, so it beats our rule above on
   specificity; this one adds a 4th to win back display and keep the dots
   always visible, even with just a handful of reviews. */
.sa-feedbacks--slider .splide:not(.is-overflow) .splide__pagination {
  display: flex;
}
.sa-feedbacks--slider .splide__arrow {
  transition: border-color 200ms var(--ease-fh), box-shadow 200ms var(--ease-fh);
}
.sa-feedbacks--slider .splide__arrow:hover {
  border-color: rgba(var(--cl-accent), 0.5);
  box-shadow: 0 0 0 3px rgba(var(--cl-accent), 0.12);
}
.sa-feedbacks--slider .splide__arrow svg {
  transition: fill 200ms var(--ease-fh);
}
.sa-feedbacks--slider .splide__arrow:hover svg {
  fill: rgb(var(--cl-accent));
}
.sa-feedbacks--slider .splide__pagination__page {
  transition: transform 200ms var(--ease-fh), box-shadow 200ms var(--ease-fh);
}
.sa-feedbacks--slider .splide__pagination__page.is-active {
  box-shadow: 0 0 6px rgba(var(--cl-accent), 0.5), 0 0 16px rgba(var(--cl-accent), 0.3);
  transform: scale(1.15);
}

/* ==========================================================================
   10. Hero — make the primary CTA impossible to miss
   Two equal-weight buttons side by side (solid "Browse products" + outline
   "View on FunPay") read as equally important, so the eye doesn't land
   anywhere. A slow breathing glow on the solid one only settles that —
   same accent colour as the rest of the site, no new hue. Pauses on hover
   (it's already getting a state change from the base .sa-btn hover) and
   sits still under reduced motion, matching every other loop on the site.
   -------------------------------------------------------------------------- */
.sa-hero-actions .sa-btn--primary.sa-btn--solid {
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.35), 0 6px 18px -6px rgba(var(--cl-accent), 0.45), 0 0 28px rgba(var(--cl-accent), 0.3);
  animation: fh-cta-breathe 2.8s var(--ease-fh) infinite;
}
.sa-hero-actions .sa-btn--primary.sa-btn--solid:hover {
  animation-play-state: paused;
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.6), 0 8px 22px -6px rgba(var(--cl-accent), 0.6), 0 0 44px rgba(var(--cl-accent), 0.5);
}
@keyframes fh-cta-breathe {
  0%, 100% { box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.35), 0 6px 18px -6px rgba(var(--cl-accent), 0.45), 0 0 26px rgba(var(--cl-accent), 0.28); }
  50%      { box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.55), 0 8px 22px -6px rgba(var(--cl-accent), 0.58), 0 0 44px rgba(var(--cl-accent), 0.48); }
}
@media (prefers-reduced-motion: reduce) {
  .sa-hero-actions .sa-btn--primary.sa-btn--solid {
    animation: none;
    box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.45), 0 6px 18px -6px rgba(var(--cl-accent), 0.5), 0 0 34px rgba(var(--cl-accent), 0.36);
  }
}

/* ==========================================================================
   11. Product cards — cursor-tracking spotlight border
   The Vercel/Linear hover: a bright arc of the border lights up right
   where the cursor is and follows it. Built as a ::before ring using the
   padding-box/border-box mask-composite:exclude trick, so only the 1px
   border stroke itself is ever painted — never a filled glow over the
   card's content. custom.js (§8) writes --fh-spot-x/-y in px on pointer-
   move; until it does, the gradient just centres on 50%/50% and stays at
   opacity 0 until :hover. Sits alongside the existing HUD-bracket motif
   (§1) rather than replacing it — brackets say "locked on", this says
   "live" under the pointer.
   Colour: rgba(var(--card-accent, var(--cl-accent)), …) — product-card.njk
   sets --card-accent inline from product.metadata.accent_color ("R,G,B")
   when present, so a product can glow its own colour on hover; with no
   metadata set it just falls through to the theme's normal accent.
   The ring line itself gets a soft glow via filter: drop-shadow (NOT
   box-shadow — box-shadow would be cut by the mask-composite trick above,
   drop-shadow is computed post-mask so it correctly hugs just the visible
   arc). Separately, .sa-product-card itself gets an ambient box-shadow
   halo on hover — same colour, layered on top of the card's normal
   shadow-card so elevation isn't lost — so the glow reads behind the
   whole card, not just along its edge.
   -------------------------------------------------------------------------- */
.sa-product-card {
  --fh-spot-x: 50%;
  --fh-spot-y: 50%;
  transition: box-shadow 260ms var(--ease-fh);
}
.sa-product-card:hover,
.sa-product-card:focus-visible {
  box-shadow: 0 0 28px -6px rgba(var(--card-accent, var(--cl-accent)), 0.4), 0 0 64px -14px rgba(var(--card-accent, var(--cl-accent)), 0.24), var(--shadow-card);
}
.sa-product-card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  padding: 1px;
  background: radial-gradient(240px circle at var(--fh-spot-x) var(--fh-spot-y), rgba(var(--card-accent, var(--cl-accent)), 0.95), transparent 68%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  filter: drop-shadow(0 0 6px rgba(var(--card-accent, var(--cl-accent)), 0.55)) drop-shadow(0 0 16px rgba(var(--card-accent, var(--cl-accent)), 0.3));
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms var(--ease-fh);
}
.sa-product-card:hover::before,
.sa-product-card:focus-visible::before {
  opacity: 1;
}
@media (hover: none) {
  .sa-product-card::before {
    display: none; /* no pointer to track on touch — the existing tap/focus states already cover it */
  }
}
@media (prefers-reduced-motion: reduce) {
  .sa-product-card,
  .sa-product-card::before {
    transition: none;
  }
}

/* ==========================================================================
   12. Product page — sliding glow bar under the active tab
   Replaces the old per-button static underline (each button carried its
   own border-b-2 that just changed colour) with one shared bar that
   physically slides to the active tab, sized and positioned by custom.js
   (§9) since the buttons are variable-width. Same gradient-fade + glow
   language as the section-title divider (§7b), so it reads as the site's
   line motif rather than a third new treatment.
   -------------------------------------------------------------------------- */
.sa-tabs {
  position: relative;
}
.sa-tabs-indicator {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgb(var(--cl-accent)) 20%, rgb(var(--cl-accent)) 80%, transparent);
  box-shadow: 0 0 8px rgba(var(--cl-accent), 0.55), 0 0 22px rgba(var(--cl-accent), 0.3);
  opacity: 0;
  pointer-events: none;
  transition: transform 320ms var(--ease-fh), width 320ms var(--ease-fh), opacity 200ms var(--ease-fh);
}
.sa-tabs-indicator.is-ready {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .sa-tabs-indicator {
    transition: opacity 200ms var(--ease-fh);
  }
}

/* ==========================================================================
   13. Stats — a landing flash when the count-up finishes
   custom.js (§5) adds .fh-stat-landed for one animation cycle the instant
   a counter (products sold / total customers) reaches its final value, so
   the number visibly "arrives" instead of just stopping mid-motion.
   -------------------------------------------------------------------------- */
.fh-stat-landed {
  animation: fh-stat-flash 650ms var(--ease-fh);
}
@keyframes fh-stat-flash {
  0%   { text-shadow: 0 0 0 rgba(var(--cl-accent), 0); }
  35%  { text-shadow: 0 0 18px rgba(var(--cl-accent), 0.7), 0 0 40px rgba(var(--cl-accent), 0.32); }
  100% { text-shadow: 0 0 0 rgba(var(--cl-accent), 0); }
}
@media (prefers-reduced-motion: reduce) {
  .fh-stat-landed {
    animation: none;
  }
}

/* ==========================================================================
   14. Add to Cart — one-shot success flash
   custom.js (§8) toggles .is-added-flash for 700ms right on click. A ring
   that expands and fades outward, paired with a small scale pop — reads
   as "confirmed" without needing to loop or persist.
   -------------------------------------------------------------------------- */
.sa-add-to-cart.is-added-flash {
  animation: fh-add-flash 650ms var(--ease-fh);
}
@keyframes fh-add-flash {
  0%   { box-shadow: 0 0 0 0 rgba(var(--cl-accent), 0.6); transform: scale(1); }
  30%  { box-shadow: 0 0 0 8px rgba(var(--cl-accent), 0), 0 0 30px rgba(var(--cl-accent), 0.55); transform: scale(1.03); }
  100% { box-shadow: 0 0 0 0 rgba(var(--cl-accent), 0); transform: scale(1); }
}
/* Every other transform-driven animation in this file gets switched off
   under reduced motion — this one (it scales, same as the bracket lift
   and the step-circle cascade) was the one gap. */
@media (prefers-reduced-motion: reduce) {
  .sa-add-to-cart.is-added-flash {
    animation: none;
  }
}

/* ==========================================================================
   15. Infinite marquee — payment-method belt + product conveyor
   You asked for the "Secure checkout" payment icons and the featured
   products to drift sideways on a loop and dissolve into the edges,
   instead of sitting still. Checked vyngg.store and divine.land for how
   they do it: both duplicate their payment-icon row and run it as one
   continuous strip rather than a static line — same idea here, just
   built so it works for however many icons/products a shop actually has.

   Mechanic: custom.js (§9) clones each `.fh-marquee-track`'s own children
   until the strip is at least 2x the viewport width, then writes
   `--fh-marquee-w` (one strip-length, in px) on the wrapper. The keyframe
   below just slides the track left by exactly that distance forever —
   since every copy is identical, the loop point is invisible no matter
   how many items exist or how wide the screen is. `mask-image` fades
   both edges to transparent instead of a hard clip, which is the
   "dissolve near the edge" part of the ask. Hover/focus pauses the drift
   so a click on an icon tooltip or a product card never gets yanked out
   from under the cursor mid-slide.
   ========================================================================== */
.fh-marquee {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
}
.fh-marquee-track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: fh-marquee-scroll var(--fh-marquee-dur, 24s) linear infinite;
  /* Promotes this element to its own compositor layer up front. Without
     it, the belt could end up "running" by every JS/CSS measurement
     (data-fh-ready set, animationPlayState: running, computed transform
     changing over time) while never actually repainting on screen —
     until something else forced a full repaint, like opening DevTools
     (which is what made it "fix itself" when checking). will-change
     tells the browser to composite this up front instead of leaving it
     on the main thread's paint schedule. */
  will-change: transform;
}
/* Paused until custom.js has measured the real strip width — otherwise
   the row would animate toward 0px (the CSS fallback) for one frame and
   visibly jump once JS sets the real distance. */
.fh-marquee-track:not([data-fh-ready]) {
  animation-play-state: paused;
}
.fh-marquee:hover .fh-marquee-track,
.fh-marquee:focus-within .fh-marquee-track {
  animation-play-state: paused;
}
@keyframes fh-marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-1 * var(--fh-marquee-w, 0px))); }
}
@media (prefers-reduced-motion: reduce) {
  .fh-marquee-track {
    animation: none !important;
    flex-wrap: wrap;
    width: 100%;
  }
  .fh-marquee {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* 15a. Payment-icon belt.
   Card variant (the one actually in use — heading on top, belt below):
   deliberately narrow and centered so only ~3 icons show at a time and
   the mask-fade (custom.css §15) is clearly visible doing its job,
   instead of stretching wide enough to show every payment method at
   once with no visible fade. Inline/split variants (if a merchant picks
   them from the editor) keep the old flex-grow behaviour, filling
   whatever row space is next to the "Secure checkout" text. */
.sa-trust-badges--card .fh-marquee {
  width: 100%;
  max-width: 15.5rem;
  margin-inline: auto;
}
/* Per your ask: no more boxed panel here — drop the border/background/
   shadow/padding so "Secure checkout" + the belt sit as plain text
   straight on the section's (transparent) background instead of inside
   a floating card. Selector is scoped to the card variant's own direct
   wrapper div, so it doesn't touch any other bordered box elsewhere. */
.sa-trust-badges--card .wrap > div {
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0;
}
.sa-trust-badges--inline .fh-marquee,
.sa-trust-badges--split .fh-marquee {
  flex: 1 1 auto;
  min-width: 0;
  align-self: stretch;
}
.fh-marquee .sa-trust-badges-items {
  flex-wrap: nowrap;
  gap: 1.75rem;
}
.fh-marquee .sa-trust-badges-items > * {
  flex: none;
}

/* 15b. Product conveyor — three cards across on desktop (per your ask),
   two on tablet, one-plus-a-peek on mobile so the "next" card is visibly
   cut off by the fade rather than the row looking like a plain carousel.
   Extra block padding/negative margin keeps the belt's overflow:hidden
   from clipping the product-card hover lift + glow (§1/§11 above). */
.sa-products--marquee .fh-marquee {
  padding-block: 0.75rem;
  margin-block: -0.75rem;
  /* Makes this element a size container, so `cqw` below can measure
     against ITS real width — see note below on why that replaced vw. */
  container-type: inline-size;
}
.sa-products--marquee .fh-marquee-track {
  gap: var(--grid-gap);
}
.fh-marquee-item {
  flex: none;
  width: min(78vw, 320px);
}
/* Percentages can't be used here — .fh-marquee-track is deliberately
   width: max-content (so it can overflow into a loopable strip), and a
   child sized as % of a max-content parent is circular. custom.js (§9)
   measures the wrapper's real, layout-constrained width and writes it
   as --fh-conveyor-w in px, which these calc()s use — so 3 cards fit
   the section's content width, not the raw viewport.

   FIX: the fallback used to be plain `100vw` — meant only as a value
   for the split-second before JS finishes measuring. But whenever JS
   didn't get a chance to run at all (the theme builder swaps a
   section's markup live without a page reload — custom.js only ran
   once, on the original page load, so a freshly-rendered belt was
   permanently stuck on this fallback), 100vw is the FULL viewport, not
   this belt's actual column width — hence cards ballooning huge and
   never animating (paused until `data-fh-ready`, which JS also never
   got to set). Falling back to `100cqw` instead — measured against the
   container above — means even a completely unmeasured belt sizes
   itself correctly immediately, no JS required. custom.js (§9) has
   also been made re-run automatically whenever new markup like this
   shows up, so this is now a safety net rather than the only line of
   defence. */
@media (min-width: 640px) {
  .fh-marquee-item { width: calc((var(--fh-conveyor-w, 100cqw) - var(--grid-gap)) / 2); }
}
@media (min-width: 1024px) {
  .fh-marquee-item { width: calc((var(--fh-conveyor-w, 100cqw) - 2 * var(--grid-gap)) / 3); }
}

/* ==========================================================================
   16. Accessibility — a real focus-visible ring, sitewide
   Nothing gave keyboard focus its own look. A couple of components colour
   their OPEN/expanded state (the currency selector), but that's not the
   same thing as a focus ring, and every other interactive element was
   just falling back to the browser default. One universal, on-brand fix:
   the same accent glow used everywhere else on hover, on `outline` rather
   than `border`/`box-shadow`, so it sits outside a component's own box
   and layers on top of whatever hover/open-state styling it already has
   instead of fighting it.
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid rgba(var(--cl-accent), 0.75);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(var(--cl-accent), 0.14);
  transition: outline-color 160ms var(--ease-fh), box-shadow 160ms var(--ease-fh);
}

/* ==========================================================================
   17. Finishing the hook coverage
   The sa-* reference at the top of this file documents .sa-searchbar,
   .sa-pagination, .sa-breadcrumbs, .sa-product-gallery, .sa-cart-panel +
   .sa-cart-item and .sa-toasts + .sa-toast as available hooks. None of
   them had picked up any of this file's styling yet — they were still on
   plain theme defaults while everything else got the considered pass.
   This section brings them into the same language: the accent glow
   already used for hover/active states (nav links, steps, the tab
   indicator), the glass-and-hairline treatment already established for
   the pill nav and Discord toast (§3, §7d), and tabular numerals for the
   other place price-like digits show up.

   A couple of these lean on a guessed state selector (flagged inline)
   since I can't see your real markup — worst case it's inert and the
   fix is a one-line selector swap, everything else here still applies.
   -------------------------------------------------------------------------- */

/* 17a. Breadcrumbs — same hover language as the nav links (§3). */
.sa-breadcrumbs a {
  transition: color 160ms var(--ease-fh);
}
.sa-breadcrumbs a:hover {
  color: rgb(var(--cl-accent));
}

/* 17b. Search bar — the exact treatment the currency selector already
   gets while open (border only — the glow comes from the new sitewide
   focus ring in §16, so a focused input doesn't end up with two
   competing glows stacked on each other). */
.sa-searchbar {
  transition: border-color 200ms var(--ease-fh);
}
.sa-searchbar:focus-within {
  border-color: rgba(var(--cl-accent), 0.5);
}

/* 17c. Pagination — reuses the numbered-circle language from "How it
   works" (§8): the current page gets the same glow ring a reached step
   gets, everything else gets the standard nav-link tint on hover.
   GUESSED SELECTOR: assumes the current page carries aria-current="page",
   the same kind of ARIA hook the currency selector (§3) already relies
   on elsewhere in this theme. */
.sa-pagination a,
.sa-pagination button {
  transition: color 160ms var(--ease-fh), background-color 160ms var(--ease-fh), box-shadow 160ms var(--ease-fh);
}
.sa-pagination a:hover,
.sa-pagination button:hover {
  color: rgb(var(--cl-accent));
  background-color: rgba(var(--cl-accent), 0.08);
}
.sa-pagination [aria-current='page'] {
  color: rgb(var(--cl-accent));
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.35), 0 0 14px rgba(var(--cl-accent), 0.22);
}

/* 17d. Product gallery — GUESSED SELECTOR: covers aria-selected and a
   plain .is-active class since I can't see which one your thumbnail
   markup actually uses; harmless if one of the two never matches. The
   selected thumbnail gets the same border-glow ring as a hovered step
   number (§8); the rest dim slightly on hover so the selected one keeps
   reading as "the one in the main image". */
.sa-product-gallery img,
.sa-product-gallery button,
.sa-product-gallery a {
  transition: opacity 160ms var(--ease-fh), border-color 160ms var(--ease-fh), box-shadow 160ms var(--ease-fh);
}
.sa-product-gallery [aria-selected='true'],
.sa-product-gallery .is-active {
  border-color: rgb(var(--cl-accent));
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.3), 0 0 14px rgba(var(--cl-accent), 0.22);
}
.sa-product-gallery button:not([aria-selected='true']):not(.is-active):hover,
.sa-product-gallery a:not([aria-selected='true']):not(.is-active):hover {
  opacity: 0.75;
}

/* 17e. Cart panel + items — the same glass surface as the pill nav and
   Discord toast (blur, accent-tinted edge), so opening the cart doesn't
   feel like a different, unstyled part of the site. tabular-nums on the
   panel is inherited by everything inside it, so line-item prices and
   the subtotal line up without needing a confirmed .sa-cart-item-price
   hook to target directly (same fix as the product-card price in §0,
   applied the safe way here since that specific hook isn't documented). */
.sa-cart-panel {
  border: 1px solid rgba(var(--cl-accent), 0.16);
  background-color: rgba(var(--cl-surface-raised), 0.85);
  backdrop-filter: blur(16px);
  font-variant-numeric: tabular-nums;
}
.sa-cart-item {
  border-bottom: 1px solid rgba(var(--cl-accent), 0.08);
  transition: background-color 160ms var(--ease-fh);
}
.sa-cart-item:last-child {
  border-bottom: none;
}
.sa-cart-item:hover {
  background-color: rgba(var(--cl-accent), 0.05);
}

/* 17f. Toasts — pulled into the same "quiet glass" family as the Discord
   toast (§5): glass background, one accent edge instead of a full
   border, and a one-shot arrival glow. The glow only ever animates
   box-shadow, never opacity or transform, on purpose: if the theme's own
   JS already handles a toast's slide/fade-in, this can't fight it for
   those properties — it just adds a flash on top of whatever motion is
   already there. */
.sa-toasts {
  z-index: 70;
}
.sa-toast {
  border: 1px solid rgba(var(--cl-accent), 0.18);
  border-left: 3px solid rgb(var(--cl-accent));
  background-color: rgba(var(--cl-surface-raised), 0.9);
  backdrop-filter: blur(16px);
  box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.5);
  animation: fh-toast-in 380ms var(--ease-fh-spring);
}
@keyframes fh-toast-in {
  0%   { box-shadow: 0 0 0 0 rgba(var(--cl-accent), 0); }
  30%  { box-shadow: 0 0 0 6px rgba(var(--cl-accent), 0.12), 0 10px 30px -12px rgba(0, 0, 0, 0.5); }
  100% { box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.5); }
}
@media (prefers-reduced-motion: reduce) {
  .sa-toast {
    animation: none;
  }
}

/* 17g. Product card status — turns plain "In stock" / "Sold out" text
   into an actual badge instead of bare text sitting in the card. Kept to
   neutral surface tones rather than guessing at red/green per state,
   since I can't see whether your markup gives each state its own
   modifier class to hook a colour to — if it does (e.g.
   .sa-product-card-status--out), that's the one-line extension to add
   next. */
.sa-product-card-status {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border-radius: 9999px;
  background-color: rgba(var(--cl-surface-raised), 0.6);
  font-size: 0.75rem;
}

/* 17h. Primary buttons, everywhere — the hero CTA (§10) was the only
   button on the whole site with any glow at all; every other primary
   button (checkout, form submits) was still on the theme's plain
   default. This gives all of them a modest hover glow and lift WITHOUT
   the infinite breathing loop, which stays exclusive to the hero so that
   one button keeps being the one your eye lands on first. Specificity
   keeps the two from colliding — the hero's own selector is more
   specific and always wins there. */
.sa-btn--primary.sa-btn--solid {
  transition: box-shadow 200ms var(--ease-fh), transform 200ms var(--ease-fh);
}
.sa-btn--primary.sa-btn--solid:hover {
  box-shadow: 0 0 0 1px rgba(var(--cl-accent), 0.4), 0 6px 16px -6px rgba(var(--cl-accent), 0.4);
  transform: translateY(-1px);
}
@media (prefers-reduced-motion: reduce) {
  .sa-btn--primary.sa-btn--solid {
    transition: none;
  }
}

/* ==========================================================================
   18. Product cards — price/stock pick up the card's own colour on hover,
   plus a matching wash along the bottom edge
   Price used to sit in the theme's flat accent colour all the time
   (text-accent-500 in product-card.njk), which fought with the per-product
   spotlight added in §11 — a card would glow blue on hover while its price
   stayed the sitewide teal regardless. Both price and the positive-stock
   line (".in stock" / "N in stock" — out-of-stock red is left alone, it's
   a status, not decoration) now start neutral and only switch to
   rgba(var(--card-accent, var(--cl-accent))) on hover/focus — same trigger,
   same colour source as the border glow, so the whole card commits to one
   colour at once instead of two clashing ones. Media-overlay cards are
   excluded on purpose: their price/stock sit in white over a photo for
   legibility, which a colour swap would undermine.
   The footer wash is a ::before behind the row (not a background-image
   transition — gradients don't interpolate smoothly on hover) bled past
   the row's own box with a negative inset so it reaches the card's real
   edges, with its bottom corners rounded to match --radius-card. Flat-
   divider's card is overflow-hidden so its inset just needs to reach or
   pass the edge; padded-detail isn't, so its inset and corner radius have
   to land on the real edge exactly (see the per-style rules below —
   different padding stacks before this row, hence different offsets).
   `.sa-product-card-footer` is only on the flat-divider and padded-detail
   markup (the ones with an actual "bottom of card" — compact-row's price
   sits in a horizontal row instead, so it only gets the colour swap above).
   -------------------------------------------------------------------------- */
.sa-product-card--flat-divider .sa-product-card-price,
.sa-product-card--padded-detail .sa-product-card-price,
.sa-product-card--compact-row .sa-product-card-price,
.sa-product-card--flat-divider .sa-product-card-stock .text-success,
.sa-product-card--padded-detail .sa-product-card-stock .text-success,
.sa-product-card--compact-row .sa-product-card-stock .text-success {
  transition: color 220ms var(--ease-fh);
}
.sa-product-card--flat-divider:hover .sa-product-card-price,
.sa-product-card--flat-divider:focus-visible .sa-product-card-price,
.sa-product-card--padded-detail:hover .sa-product-card-price,
.sa-product-card--padded-detail:focus-visible .sa-product-card-price,
.sa-product-card--compact-row:hover .sa-product-card-price,
.sa-product-card--compact-row:focus-visible .sa-product-card-price,
.sa-product-card--flat-divider:hover .sa-product-card-stock .text-success,
.sa-product-card--flat-divider:focus-visible .sa-product-card-stock .text-success,
.sa-product-card--padded-detail:hover .sa-product-card-stock .text-success,
.sa-product-card--padded-detail:focus-visible .sa-product-card-stock .text-success,
.sa-product-card--compact-row:hover .sa-product-card-stock .text-success,
.sa-product-card--compact-row:focus-visible .sa-product-card-stock .text-success {
  color: rgb(var(--card-accent, var(--cl-accent)));
}

.sa-product-card-footer {
  position: relative;
  z-index: 0;
}
.sa-product-card-footer::before {
  content: '';
  position: absolute;
  z-index: -1;
  border-radius: 0 0 var(--radius-card) var(--radius-card);
  background: linear-gradient(to bottom, transparent, rgba(var(--card-accent, var(--cl-accent)), 0.16));
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms var(--ease-fh);
}
/* flat-divider's footer row sits inside a single 1rem (.p-4) wrapper. */
.sa-product-card--flat-divider .sa-product-card-footer::before {
  inset: 0 -1rem -1rem -1rem;
}
/* padded-detail stacks two paddings before this row (.p-2.5 on the card,
   .px-2/.pb-1.5 on the inner wrapper) — and unlike flat-divider it has no
   overflow-hidden of its own to fall back on, so this needs to land on the
   real edge exactly rather than rely on being clipped back to it. */
.sa-product-card--padded-detail .sa-product-card-footer::before {
  inset: 0 -1.125rem -1rem -1.125rem;
}
.sa-product-card:hover .sa-product-card-footer::before,
.sa-product-card:focus-visible .sa-product-card-footer::before {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .sa-product-card--flat-divider .sa-product-card-price,
  .sa-product-card--padded-detail .sa-product-card-price,
  .sa-product-card--compact-row .sa-product-card-price,
  .sa-product-card--flat-divider .sa-product-card-stock .text-success,
  .sa-product-card--padded-detail .sa-product-card-stock .text-success,
  .sa-product-card--compact-row .sa-product-card-stock .text-success,
  .sa-product-card-footer::before {
    transition: none;
  }
}

/* Прячем фоновое изображение магазина на витрине — 
   оно всё равно останется доступно для чекаута через 
   переключатель "Use Storefront Background Image" в настройках SellAuth */
body > div[style*="background-image"].pointer-events-none {
  display: none !important;
}

/* Прицел вместо курсора — кольцо точек крутится, крестик в центре
   стоит на месте. На телефонах/планшетах не рендерится вообще. */
.fh-cursor{
  position: fixed;
  top: 0; left: 0;
  width: 0; height: 0;
  z-index: 9999;
  pointer-events: none;
  transform: translate(-100px,-100px);
  will-change: transform;
  filter: drop-shadow(0 0 1px rgba(0,0,0,.9)) drop-shadow(0 0 3px rgba(0,0,0,.45));
}
.fh-cursor-ring{
  position: absolute; top: 0; left: 0;
  width: 40px; height: 40px;
  margin: -20px 0 0 -20px;
  animation: fh-cursor-spin 6s linear infinite;
}
.fh-dot{
  position: absolute; top: 50%; left: 50%;
  width: 3px; height: 3px;
  margin: -1.5px 0 0 -1.5px;
  border-radius: 50%;
  background: rgb(var(--cl-accent));
  transform: rotate(calc(var(--i) * 30deg)) translateX(20px);
}
.fh-cursor-cross{ position: absolute; top: 0; left: 0; }
.fh-cross-h, .fh-cross-v{ position: absolute; background: rgba(var(--cl-accent), .9); }
.fh-cross-h{ width: 8px; height: 1px; top: -0.5px; left: -4px; }
.fh-cross-v{ width: 1px; height: 8px; top: -4px; left: -0.5px; }
@keyframes fh-cursor-spin{ to { transform: rotate(360deg); } }

body.fh-cursor-active, body.fh-cursor-active *{ cursor: none !important; }

@media (hover: none), (pointer: coarse){
  .fh-cursor{ display: none !important; }
}
@media (prefers-reduced-motion: reduce){
  .fh-cursor-ring{ animation: none; }
}

.sa-navbar-link.bg-t-primary\/5 {
  position: relative;
  background: transparent;
}
.sa-navbar-link.bg-t-primary\/5::before,
.sa-navbar-link.bg-t-primary\/5::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -2px;
  transform: translateX(-50%);
  width: calc(100% - 0.25rem);
  background: linear-gradient(90deg, transparent, rgb(var(--cl-accent)) 50%, transparent);
  pointer-events: none;
}
.sa-navbar-link.bg-t-primary\/5::after {
  height: 2px;
}
.sa-navbar-link.bg-t-primary\/5::before {
  height: 12px;
  bottom: -7px;
  filter: blur(5px);
  opacity: .85;
}

.sa-btn--primary.sa-btn--solid {
  position: relative;
  transition: box-shadow 220ms var(--ease-fh), background-color var(--dur) ease, transform 220ms var(--ease-fh);
}
.sa-btn--primary.sa-btn--solid:hover,
.sa-btn--primary.sa-btn--solid:focus-visible {
  box-shadow: 0 0 16px 2px rgba(var(--cl-accent), .45), 0 0 32px 8px rgba(var(--cl-accent), .2);
}

/* ==========================================================================
   19. Product page — corner glow picks up the product's own accent colour
   -------------------------------------------------------------------------- */
.pfx-glows::before,
.pfx-glows-corners::after {
  background: radial-gradient(circle, rgba(var(--pfx-accent, var(--cl-accent)), var(--pfx-glow-a, 0.1)), transparent 65%);
}