/* --------------------------------------------------------------------------
   TIER 1 TOKENS -- ground and scale, shared by every product.
   Declared on .hs-root (not :root) so they cannot leak into the rest of your
   WordPress site, and your theme cannot accidentally win a specificity fight.
   Signature colours (tier 2) live in each product's theme.css.
   -------------------------------------------------------------------------- */
.hs-root {
  /* Shell -- Theme.h Scheme::Blue */
  --rp-bg:        #0a0b11;
  --rp-panel:     #13141d;
  --rp-card:      #191a24;
  --rp-card2:     #20212d;
  --rp-border:    #292b39;
  --rp-border2:   #393c4e;
  --rp-text:      #eceef6;
  --rp-text2:     #969ab0;
  --rp-text3:     #5e617a;

  /* Type scale -- fluid, clamp() so it never needs a media query */
  --rp-f-display: clamp(2.6rem, 6.2vw, 5.1rem);
  --rp-f-h2:      clamp(1.95rem, 3.7vw, 3.05rem);
  --rp-f-h3:      clamp(1.15rem, 1.6vw, 1.4rem);
  --rp-f-lede:    clamp(1.05rem, 1.35vw, 1.24rem);
  --rp-f-body:    clamp(1rem, 1.05vw, 1.09rem);
  --rp-f-small:   0.875rem;
  --rp-f-micro:   0.845rem;   /* eyebrows / labels: was 0.72rem (+2pt) */

  /* Type families -- tier 1 so a product can restyle its whole page from
     its own theme.css by overriding two properties, instead of the ~20
     places base.css used to name a family literally. Whatever a product
     substitutes must also be added to the FONTS <link> in build.py, or the
     browser falls through to the next family in the stack. */
  --rp-font-display: "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --rp-font-mono:    "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Typographic feel -- line-height and tracking, tunable like the sizes */
  --rp-lh-head:   1.06;
  --rp-lh-body:   1.62;
  --rp-ls-head:   -0.028em;

  /* Rhythm */
  --rp-max:       1180px;
  --rp-gutter:    clamp(1.15rem, 4vw, 2.5rem);
  --rp-section:   clamp(4.5rem, 9vw, 8.5rem);
  --rp-r-sm:      10px;
  --rp-r:         16px;
  --rp-r-lg:      22px;

  /* Motion -- fast easings so hovers feel snappy, not floaty */
  --rp-fast:   140ms cubic-bezier(.22,.61,.36,1);
  --rp-med:    240ms cubic-bezier(.22,.61,.36,1);
  --rp-slow:   620ms cubic-bezier(.16,.84,.44,1);
  --rp-spring: 420ms cubic-bezier(.34,1.42,.64,1);
}

/* ==========================================================================
   HARDSTOP SALES PAGE -- structural stylesheet (tier 4 of the cascade)
   Tokens now live in three tiers, assembled ahead of this file by
   stylesheet() in build.py:
     tier 1  shared/css/tokens.css        ground + scale, every product
     tier 2  products/<slug>/theme.css    eight signature values
     tier 3  products/<slug>/section.css  product-local extras (optional)
   This file consumes those custom properties; it declares none of its own.
   ========================================================================== */

/* @property lets the browser interpolate a custom property instead of
   snapping between values. Without this the knob arc jumps; with it, it
   sweeps. Unsupported browsers ignore the block and just snap -- no harm. */
@property --arc { syntax: '<angle>'; inherits: true; initial-value: 0turn; }

/* --------------------------------------------------------------------------
   2. RESET -- scoped. Divi injects a lot of global CSS; this neutralises it
   inside our block only, without touching the rest of the site.
   -------------------------------------------------------------------------- */
.hs-root,
.hs-root *,
.hs-root *::before,
.hs-root *::after { box-sizing: border-box; }

.hs-root {
  margin: 0;
  padding: 0;
  background: var(--rp-bg);
  color: var(--rp-text);
  font-family: var(--rp-font-display);
  font-size: var(--rp-f-body);
  line-height: var(--rp-lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
  position: relative;
  isolation: isolate;           /* keeps our z-indexes out of Divi's stack */
}

/* :where() contributes ZERO specificity, so every one of these rules weighs
   exactly as much as ".hs-root" alone (0,1,0) instead of (0,1,1).
   That matters: written the naive way, ".hs-root p { margin: 0 }" would beat
   ".hs-price__sub { margin-bottom: 1.5rem }" -- same class count, but the
   element selector tips it -- and component spacing would silently vanish.
   With :where() the reset still overpowers Divi's global styles but always
   loses to our own component classes. This is the single most useful
   specificity trick in modern CSS. */
.hs-root :where(h1, h2, h3, h4, p, ul, ol, li, figure, dl, dd, dt) {
  margin: 0; padding: 0; border: 0;
}
.hs-root :where(ul, ol) { list-style: none; }

/* Tables are the one place :where() is the WRONG tool. Divi ships global table
   chrome -- `table { border: 1px solid #eee; margin: 0 0 15px }` plus th/td
   borders and padding -- and some of those rules are class-scoped (0,1,1), so a
   zero-specificity :where() reset (0,1,0) would lose to them. These are written
   with a real element selector (0,1,1) instead: they tie with Divi and win on
   source order, because this <style> block is injected after the theme's.
   Left unreset, Divi's #eee border painted a bright rectangle around .hs-table
   and its 15px bottom margin dropped .hs-tablewrap's rounded border a row's
   worth below the table's actual edge -- the two outlines visibly came apart.
   Everything here is re-set properly by section 14; this only clears the theme.
   Section 14 sits later in the file, so it wins the same source-order tie. */
.hs-root table { border: 0; border-collapse: collapse; border-spacing: 0; margin: 0; background: none; }
.hs-root caption,
.hs-root thead, .hs-root tbody, .hs-root tfoot,
.hs-root tr, .hs-root th, .hs-root td {
  margin: 0; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; text-align: inherit; vertical-align: inherit;
}

.hs-root :where(h1, h2, h3, h4) {
  font-weight: 700;
  line-height: var(--rp-lh-head);
  letter-spacing: var(--rp-ls-head);
  color: var(--rp-text);
}

.hs-root :where(img, svg, canvas, video, iframe) {
  display: block; max-width: 100%; height: auto;
}

/* :where() drops this to zero-for-the-selector specificity, so a component that
   sets its own colour (.hs-btn, below) wins even though it is also 1 class deep.
   Without it, .hs-root a (0,1,1) beat .hs-btn (0,1,0) and every filled button
   inherited body-text white instead of its dark --btn-fg. */
.hs-root :where(a) { color: inherit; text-decoration: none; }
.hs-root button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

.hs-root ::selection { background: var(--rp-accent); color: #04060c; }

.hs-root :focus-visible {
  outline: 2px solid var(--rp-accent);
  outline-offset: 3px;
  border-radius: 6px;
}

/* --------------------------------------------------------------------------
   3. LAYOUT PRIMITIVES
   -------------------------------------------------------------------------- */
.hs-wrap {
  width: 100%;
  max-width: var(--rp-max);
  margin-inline: auto;
  padding-inline: var(--rp-gutter);
}
.hs-wrap--wide { max-width: 1400px; }

.hs-section { padding-block: var(--rp-section); position: relative; }
.hs-section--tight { padding-block: clamp(3rem, 5vw, 4.5rem); }

/* Hairline separators that fade at the edges -- subtler than a full border */
.hs-rule {
  height: 1px; border: 0; margin: 0;
  background: linear-gradient(90deg,
    transparent, var(--rp-border) 18%, var(--rp-border) 82%, transparent);
}

.hs-grid { display: grid; gap: clamp(1rem, 2vw, 1.6rem); }
.hs-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); }
.hs-grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }

.hs-center { text-align: center; }
/* Third-party storefront scripts (Audioloom's embedded storefront among them)
   show and hide their own elements by toggling the `hidden` attribute, and
   rely on the page having a rule that honours it. The UA default is weak
   enough that any display value overrides it, so this needs the !important
   the vendor asks for. No markup this repo generates carries the attribute -
   it exists for injected elements only. */
[hidden] { display: none !important; }
/* Visually hidden, still announced. For text that qualifies a visible label
   (e.g. spelling out what an "Official video" badge means) without repeating
   it on screen. */
.hs-vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.hs-measure { max-width: 62ch; }
.hs-measure-tight { max-width: 48ch; }
.hs-center .hs-measure,
.hs-center .hs-measure-tight { margin-inline: auto; }

/* --------------------------------------------------------------------------
   4. TYPOGRAPHY HELPERS
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   SALE - banner, struck price, countdown

   SHARED, not per-product. These were written in clarity-delay/section.css
   because Clarity Delay was the only product with a sale block; the markup
   that uses them is emitted by the shared pricing.j2, so ANY product running
   a sale rendered this banner and only one product had the CSS to paint it.
   Every colour here comes from --rp-accent, which each theme declares, so the
   banner takes on whichever product it appears in.
   -------------------------------------------------------------------------- */
.hs-sale {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .75rem 1.1rem;
  margin: 0 auto clamp(1.5rem, 3vw, 2.2rem);
  padding: .7rem 1.15rem;
  width: fit-content; max-width: 100%;
  /* No longer a pill: the countdown sits on its own line below the price, so
     a 999px radius would bow the sides of a two-line box. */
  border-radius: var(--rp-r-lg);
  border: 1px solid color-mix(in srgb, var(--rp-accent) 45%, transparent);
  background: color-mix(in srgb, var(--rp-accent) 9%, transparent);
}
.hs-sale__tag {
  font-family: var(--rp-font-mono);
  font-size: .62rem; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--rp-on-accent); background: var(--rp-accent);
  border-radius: 999px; padding: .34em .8em;
}
.hs-sale__price { font-size: 1.05rem; color: var(--rp-text); }
.hs-sale__price s {
  color: var(--rp-text2); margin-right: .15em; text-decoration-thickness: .08em;
}
.hs-sale__price b { color: var(--rp-accent); font-weight: 700; }
/* OWN LINE. flex-basis:100% breaks the row inside the wrapping flex parent,
   and the hairline above separates the deadline from the price rather than
   letting the two read as one run-on strip. */
.hs-sale__clock {
  flex-basis: 100%; justify-content: center;
  padding-top: .62rem;
  border-top: 1px solid color-mix(in srgb, var(--rp-accent) 24%, transparent);
  display: inline-flex; align-items: baseline; gap: .5em;
  font-family: var(--rp-font-mono);
}
.hs-sale__clock-label {
  font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--rp-text3);
}
/* Tabular figures so the seconds ticking over does not shift the layout. */
.hs-sale__clock-val {
  font-size: .95rem; font-weight: 700; color: var(--rp-text);
  font-variant-numeric: tabular-nums;
}
/* The price it WAS. Sized absolutely, not in em: em here resolves against
   .hs-price__amt, whose font-size is the card's - the 2.6-3.5rem figure lives
   on the sibling <b> - so .55em rendered this at about 9px with a strike too
   thin to read. Set to sit clearly below the new price but well above the
   "one-time" note, and lifted from --rp-text3 to --rp-text2: struck text is
   already visually demoted, and dimming it as well made it unreadable. */
.hs-price__was {
  color: var(--rp-text2); font-size: clamp(1.15rem, 1.7vw, 1.45rem);
  font-weight: 500; margin-right: .18em; text-decoration-thickness: .07em;
}

/* THE SAVING, next to a struck-through price. Shared rather than per-product:
   the hub and every product page all render it, and a second copy in a
   product's section.css is a second place for the two to drift apart.

   Deliberately quieter than the sale price it follows. The new price is the
   number that closes the sale; this only tells a shopper how to feel about it,
   and two loud figures side by side make neither read. */
.hs-save {
  /* --rp-accent is declared by a PRODUCT's theme, so it is undefined on the
     hub's deal banner, which sits outside any card. An undefined var inside
     color-mix() invalidates the whole declaration - border, background and
     colour all silently drop and the chip renders as plain white text. The
     fallback chain keeps it painted everywhere: the product's accent inside a
     themed card, the house accent on the hub, a literal as the last resort. */
  --save-ink: var(--rp-accent, var(--hub-accent, #7c5cff));
  display: inline-block; white-space: nowrap; vertical-align: baseline;
  margin-left: .45em;
  padding: .18em .5em; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--save-ink) 40%, transparent);
  background: color-mix(in srgb, var(--save-ink) 12%, transparent);
  color: var(--save-ink);
  font-family: var(--rp-font-mono); font-size: .64rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; line-height: 1.3;
}
/* On the price card it is a flex item beside a 3rem figure - `display:block`
   would do nothing there, so it is aligned and spaced instead. margin-left
   separates it from the price note it follows ("one-time"). */
.hs-save--card { align-self: center; margin-left: .55rem; }
/* The featured card is themed in --rp-brand throughout - its rule, glow, flag,
   ticks and button all use it - so a pill in --rp-accent reads as a stray
   element from another palette. HardStop is the case that shows it: accent is
   Drive blue, brand is clip red, and the blue pill sat alone on a red card. */
.hs-price--feature .hs-save { --save-ink: var(--rp-brand); }
/* .hs-price__amt span paints EVERY span in the price row --rp-text3 at .9rem,
   and (0,1,1) outranks .hs-save's own (0,1,0) - so inside a price card the
   pill lost both its colour and its size and came out grey and oversized.
   Re-asserted here at (0,2,0) rather than by reordering the file, which would
   only hide the collision until the next rule lands between them. */
.hs-price__amt .hs-save { color: var(--save-ink); font-size: .64rem; }

/* Struck price inside a button, for a CTA that quotes a price. */
.hs-btn__was {
  opacity: .62; font-weight: 500; margin-right: .12em;
  text-decoration-thickness: .07em;
}

.hs-eyebrow {
  font-family: var(--rp-font-mono);
  font-size: var(--rp-f-micro);
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--rp-accent);
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
}
.hs-eyebrow::before {
  content: "";
  width: 22px; height: 2px;
  background: currentColor;
  border-radius: 2px;
  opacity: 0.85;
}
.hs-center .hs-eyebrow::before { display: none; }
/* One step up from the standard eyebrow, with the tracking eased off so the
   extra size does not turn into extra sprawl. */
.hs-eyebrow--lg {
  font-size: calc(var(--rp-f-micro) * 1.22);
  letter-spacing: 0.16em;
  color: var(--rp-accent-soft);
}

/* text-wrap:balance lets the browser even out the lines of a heading instead of
   filling greedily and leaving one orphaned word on the last line -- the job a
   hand-placed <br> used to do, except it re-solves itself at every window
   width. Browsers that don't support it simply wrap the old way, and it is
   capped at a few lines by spec so it never costs layout time on body copy.
   `pretty` is the cheaper cousin for paragraphs: it only fixes the last line. */
.hs-display { font-size: var(--rp-f-display); letter-spacing: -0.04em; text-wrap: balance; }
.hs-h2      { font-size: var(--rp-f-h2); text-wrap: balance; }
.hs-h3      { font-size: var(--rp-f-h3); letter-spacing: -0.018em; text-wrap: balance; }

.hs-lede { font-size: var(--rp-f-lede); color: var(--rp-text2); line-height: calc(var(--rp-lh-body) + .04); text-wrap: pretty; }
.hs-body { color: var(--rp-text2); }
.hs-mono {
  font-family: var(--rp-font-mono);
  font-size: var(--rp-f-small);
  letter-spacing: 0.01em;
}
.hs-dim  { color: var(--rp-text3); }
.hs-white{ color: var(--rp-text); }

/* Gradient wordmark treatment matching the plugin logo's white->red split */
.hs-grad {
  background: linear-gradient(96deg, var(--rp-text) 0%, var(--rp-text) 42%, var(--rp-brand) 78%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.hs-accent-txt  { color: var(--rp-accent); }
.hs-clip-txt    { color: var(--rp-brand); }
.hs-safety-txt  { color: var(--rp-good); }
.hs-ceiling-txt { color: var(--rp-accent-2); }

/* --------------------------------------------------------------------------
   5. AMBIENT BACKGROUND -- the studio-glow feel, done in CSS so it costs
   nothing to download. Two blurred radial "lamps" plus a fine grid.
   -------------------------------------------------------------------------- */
.hs-atmos { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.hs-atmos { opacity: var(--rp-atmos-strength, 1); }
.hs-atmos::before,
.hs-atmos::after {
  content: "";
  position: absolute;
  width: 62vw; aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(110px);
  opacity: 0.4;
}
.hs-atmos::before { top: -22%; left: -14%; background: radial-gradient(circle, var(--rp-accent), transparent 66%); }
.hs-atmos::after  { top: 6%;  right: -18%; background: radial-gradient(circle, var(--rp-brand),   transparent 66%); opacity: .3; }

.hs-mesh {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(var(--rp-border2) 1px, transparent 1px),
    linear-gradient(90deg, var(--rp-border2) 1px, transparent 1px);
  background-size: 62px 62px;
  opacity: var(--rp-mesh-strength, 0.13);
  mask-image: radial-gradient(ellipse 80% 62% at 50% 34%, #000 12%, transparent 76%);
  -webkit-mask-image: radial-gradient(ellipse 80% 62% at 50% 34%, #000 12%, transparent 76%);
}

.hs-noise {
  position: absolute; inset: 0; pointer-events: none; z-index: 1; opacity: var(--rp-noise-strength, .16);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

/* --------------------------------------------------------------------------
   6. NAV -- hidden at top, slides down once you scroll past the hero
   -------------------------------------------------------------------------- */
.hs-nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90;
  background: color-mix(in srgb, var(--rp-bg) 80%, transparent);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid transparent;
  transform: translateY(-104%);
  transition: transform var(--rp-med), border-color var(--rp-med);
  will-change: transform;
}
.hs-nav.is-visible { transform: translateY(0); border-bottom-color: var(--rp-border); }
.hs-nav__inner {
  max-width: var(--rp-max); margin-inline: auto;
  padding: 0.7rem var(--rp-gutter);
  display: flex; align-items: center; gap: 1.5rem;
}
.hs-nav__logo { width: 108px; flex: none; }
.hs-nav__logo svg { width: 100%; height: auto; }
.hs-nav__links { display: flex; gap: 1.65rem; margin-left: auto; }
.hs-nav__links a {
  font-size: var(--rp-f-small); color: var(--rp-text2);
  position: relative; padding-block: 3px;
  transition: color var(--rp-fast);
}
.hs-nav__links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px;
  background: var(--rp-accent); border-radius: 2px;
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--rp-med);
}
.hs-nav__links a:hover { color: var(--rp-text); }
.hs-nav__links a:hover::after { transform: scaleX(1); }
@media (max-width: 880px) { .hs-nav__links { display: none; } .hs-nav__inner > .hs-btn { margin-left: auto; } }

/* --------------------------------------------------------------------------
   7. BUTTONS -- the single most-touched interactive element, so the states
   get the most care. Sheen sweep on hover, 1px press travel on :active.
   -------------------------------------------------------------------------- */
.hs-btn {
  --btn-bg: var(--rp-accent);
  --btn-fg: #04060c;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: .6em;
  padding: 0.92em 1.75em;
  border-radius: 999px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-weight: 700;
  font-size: 0.97rem;
  letter-spacing: -0.004em;
  white-space: nowrap;
  /* .hs-root sets antialiased, which thins strokes. That helps light text on a
     dark page but hurts DARK text on a bright fill -- exactly what a filled
     button is. Back to the platform default here so the label stays solid. */
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
  overflow: hidden;
  isolation: isolate;
  transition: transform var(--rp-fast), box-shadow var(--rp-med), background var(--rp-fast);
  /* The glow is derived from --btn-bg rather than hardcoded, so retinting
     the brand colour retints the shadow with it. color-mix() is what makes
     this possible without a preprocessor. */
  box-shadow: 0 1px 0 rgba(255,255,255,.22) inset,
              0 6px 22px -8px color-mix(in srgb, var(--btn-bg) 62%, transparent);
}
.hs-btn::after {                       /* the sheen */
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(105deg, transparent 32%, rgba(255,255,255,.42) 50%, transparent 68%);
  transform: translateX(-120%);
  transition: transform 620ms cubic-bezier(.22,.61,.36,1);
}
.hs-btn:hover  { transform: translateY(-2px); box-shadow: 0 1px 0 rgba(255,255,255,.28) inset, 0 14px 34px -10px color-mix(in srgb, var(--btn-bg) 78%, transparent); }
.hs-btn:hover::after { transform: translateX(120%); }
.hs-btn:active { transform: translateY(0); transition-duration: 60ms; }

.hs-btn--pro    { --btn-bg: var(--rp-brand); }

.hs-btn--ghost {
  --btn-fg: var(--rp-text);
  /* light-on-dark again, so the thinning is wanted back */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: rgba(255,255,255,.035);
  border: 1px solid var(--rp-border2);
  box-shadow: none;
  backdrop-filter: blur(6px);
}
.hs-btn--ghost:hover { background: rgba(255,255,255,.075); border-color: var(--rp-text3); box-shadow: none; }

.hs-btn--sm  { padding: 0.66em 1.25em; font-size: .875rem; }
.hs-btn--lg  { padding: 1.05em 2.1em; font-size: 1.03rem; }
.hs-btn--full{ width: 100%; }

.hs-btn__ico { width: 1.05em; height: 1.05em; flex: none; transition: transform var(--rp-spring); }
.hs-btn:hover .hs-btn__ico--down  { transform: translateY(2px); }
.hs-btn:hover .hs-btn__ico--right { transform: translateX(3px); }

.hs-cta-row { display: flex; flex-wrap: wrap; gap: .85rem; }
.hs-center .hs-cta-row { justify-content: center; }

/* --------------------------------------------------------------------------
   8. CARD -- cursor-tracked glow. JS writes --mx/--my as percentages; the
   ::before layer is a radial gradient anchored to those, faded in on hover.
   This is why the glow follows the pointer instead of just switching on.
   -------------------------------------------------------------------------- */
.hs-card {
  position: relative;
  background: linear-gradient(168deg, var(--rp-card) 0%, var(--rp-panel) 100%);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  padding: clamp(1.4rem, 2.4vw, 1.95rem);
  overflow: hidden;
  isolation: isolate;
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
}
.hs-card::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%),
              color-mix(in srgb, var(--rp-glow, var(--rp-accent)) var(--rp-cardglow, 17%), transparent), transparent 62%);
  opacity: 0; transition: opacity var(--rp-med);
}
.hs-card:hover {
  transform: translateY(-4px);
  border-color: var(--rp-border2);
  box-shadow: 0 22px 48px -26px rgba(0,0,0,.9);
}
.hs-card:hover::before { opacity: 1; }

.hs-card__ico {
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 13px;
  background: color-mix(in srgb, var(--rp-glow, var(--rp-accent)) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--rp-glow, var(--rp-accent)) 30%, transparent);
  color: var(--rp-glow, var(--rp-accent));
  margin-bottom: 1.1rem;
  transition: transform var(--rp-spring), background var(--rp-med);
}
.hs-card:hover .hs-card__ico { transform: translateY(-2px) scale(1.06) rotate(-4deg); }
.hs-card__ico svg { width: 23px; height: 23px; }
.hs-card h3 { margin-bottom: .5rem; font-size: 1.11rem; }
.hs-card p  { color: var(--rp-text2); font-size: .955rem; line-height: 1.62; }

/* Per-card accent -- mirrors how the plugin colour-codes its own controls */
.hs-g-accent  { --rp-glow: var(--rp-accent); }
.hs-g-safety  { --rp-glow: var(--rp-good); }
.hs-g-clip    { --rp-glow: var(--rp-brand); }
.hs-g-ceiling { --rp-glow: var(--rp-accent-2); }
.hs-g-danger  { --rp-glow: var(--rp-warn); }
/* .hs-g-brass -- HardStop's own tier-3 waveform accent; declared in
   section.css so it travels with --rp-brass instead of forcing every
   product to declare a colour it has no use for. */

/* --------------------------------------------------------------------------
   9. HERO
   -------------------------------------------------------------------------- */
.hs-hero {
  position: relative;
  padding-top: clamp(3.5rem, 8vw, 6.5rem);
  padding-bottom: clamp(3rem, 6vw, 5rem);
  text-align: center;
  overflow: hidden;
}
.hs-hero__inner { position: relative; z-index: 2; }

.hs-hero__logo { width: min(430px, 74vw); margin: 0 auto clamp(1.4rem, 3vw, 2.1rem); }
.hs-hero__logo svg { width: 100%; height: auto; filter: drop-shadow(0 6px 26px color-mix(in srgb, var(--rp-brand) 32%, transparent)); }

.hs-hero h1 { margin-bottom: 1.1rem; }
.hs-hero .hs-lede { margin: 0 auto 1.9rem; max-width: 54ch; }

.hs-badge-row { display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem; margin-bottom: 1.5rem; }
.hs-badge {
  display: inline-flex; align-items: center; gap: .45em;
  padding: .38em .85em;
  border-radius: 999px;
  border: 1px solid var(--rp-border2);
  background: rgba(255,255,255,.03);
  font-family: var(--rp-font-mono);
  font-size: var(--rp-f-micro);
  letter-spacing: .11em; text-transform: uppercase;
  color: var(--rp-text2);
  transition: border-color var(--rp-fast), color var(--rp-fast), background var(--rp-fast);
}
.hs-badge:hover { border-color: var(--rp-accent); color: var(--rp-text); background: color-mix(in srgb, var(--rp-accent) 11%, transparent); }
.hs-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--rp-good); flex: none; }
.hs-badge--free { border-color: color-mix(in srgb, var(--rp-good) 45%, transparent); color: var(--rp-good); background: color-mix(in srgb, var(--rp-good) 10%, transparent); }

.hs-trust { display: flex; flex-wrap: wrap; justify-content: center; gap: .55rem 1.6rem; margin-top: 2.15rem; }
.hs-trust li {
  display: inline-flex; align-items: center; gap: .45em;
  font-size: var(--rp-f-small); color: var(--rp-text3);
}
.hs-trust svg { width: 15px; height: 15px; color: var(--rp-good); flex: none; }

/* Hero shot: perspective tilt that eases flat as it scrolls into view */
.hs-shot {
  position: relative;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  perspective: 1800px;
}
.hs-shot__frame {
  position: relative;
  border-radius: var(--rp-r-lg);
  overflow: hidden;
  border: 1px solid var(--rp-border2);
  background: var(--rp-panel);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.03) inset,
    0 40px 90px -40px rgba(0,0,0,.95),
    0 0 90px -30px color-mix(in srgb, var(--rp-accent) 40%, transparent);
  transform: rotateX(9deg) scale(.965);
  transform-origin: 50% 100%;
  transition: transform 1.1s cubic-bezier(.16,.84,.44,1), box-shadow var(--rp-slow);
}
.hs-shot.is-in .hs-shot__frame { transform: rotateX(0deg) scale(1); }
.hs-shot__frame img { width: 100%; }

/* The blue/red rim light around the hero shot, matching the plugin's own glow */
.hs-shot__glow {
  /* inset was -14% -6%: the -6% pushed this 3px past the viewport edge on a phone,
     which is exactly the kind of overflow you should fix at the source rather than
     paper over with a page-level clip. */
  position: absolute; inset: -14% 0;
  z-index: -1; pointer-events: none;
  background:
    radial-gradient(50% 60% at 12% 50%, color-mix(in srgb, var(--rp-accent) 46%, transparent), transparent 68%),
    radial-gradient(50% 60% at 88% 50%, color-mix(in srgb, var(--rp-brand) 42%, transparent), transparent 68%);
  filter: blur(52px);
  opacity: 0; transition: opacity 1.2s ease;
}
.hs-shot.is-in .hs-shot__glow { opacity: 1; animation: hs-breathe 7s ease-in-out infinite 1.2s; }
@keyframes hs-breathe { 0%,100% { opacity: .82; } 50% { opacity: 1; } }

/* Live canvas waveform strip under the hero shot */
.hs-wavebar {
  margin-top: 1.15rem;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r-sm);
  background: linear-gradient(180deg, color-mix(in srgb, var(--rp-panel) 70%, #000), color-mix(in srgb, var(--rp-bg) 88%, #000));
  padding: .55rem .7rem .4rem;
  position: relative;
  overflow: hidden;
}
.hs-wavebar canvas { width: 100%; height: 110px; display: block; }
.hs-wavebar__legend {
  display: flex; flex-wrap: wrap; gap: .35rem 1.15rem;
  padding-top: .45rem;
  font-family: var(--rp-font-mono);
  font-size: 0.66rem; letter-spacing: .1em; text-transform: uppercase; color: var(--rp-text3);
}
.hs-wavebar__legend span { display: inline-flex; align-items: center; gap: .4em; }
.hs-wavebar__legend i { width: 9px; height: 9px; border-radius: 2px; display: block; }

/* --------------------------------------------------------------------------
   10. SPEC / STAT STRIPS
   -------------------------------------------------------------------------- */
.hs-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  overflow: hidden;
  background: var(--rp-panel);
}
.hs-stat {
  padding: clamp(1.3rem, 2.4vw, 1.9rem) 1.35rem;
  border-right: 1px solid var(--rp-border);
  position: relative;
  transition: background var(--rp-med);
}
.hs-stat:last-child { border-right: 0; }
.hs-stat:hover { background: var(--rp-card); }
.hs-stat::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--rp-glow, var(--rp-accent));
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--rp-med);
}
.hs-stat:hover::after { transform: scaleX(1); }
.hs-stat__n {
  font-family: var(--rp-font-mono);
  font-size: clamp(1.75rem, 3.1vw, 2.4rem);
  font-weight: 700; line-height: 1;
  color: var(--rp-glow, var(--rp-accent));
  letter-spacing: -.03em;
  display: block; margin-bottom: .3rem;
  font-variant-numeric: tabular-nums;
}
/* The number is the proof; the title is the claim it proves. Splitting them
   lets the card name a feature without giving up the numeral that makes the
   row scan as a row. */
.hs-stat__t {
  display: block;
  font-size: var(--rp-f-small);
  font-weight: 700;
  color: var(--rp-text);
  letter-spacing: -.012em;
  margin-bottom: .32rem;
}
.hs-stat__l { font-size: .845rem; color: var(--rp-text2); line-height: 1.45; }
@media (max-width: 640px) { .hs-stat { border-right: 0; border-bottom: 1px solid var(--rp-border); } }

/* --------------------------------------------------------------------------
   11. SPLIT (image + copy)
   -------------------------------------------------------------------------- */
.hs-split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr));
  gap: clamp(2rem, 5vw, 4.2rem);
  align-items: center;
}
.hs-split--flip > :first-child { order: 2; }
@media (max-width: 860px) { .hs-split--flip > :first-child { order: 0; } }

.hs-figure {
  position: relative;
  border-radius: var(--rp-r);
  overflow: hidden;
  border: 1px solid var(--rp-border2);
  box-shadow: 0 30px 60px -34px rgba(0,0,0,.92);
  transition: transform var(--rp-slow), box-shadow var(--rp-slow);
}
.hs-figure img { width: 100%; transition: transform 900ms cubic-bezier(.16,.84,.44,1); }
.hs-figure:hover { box-shadow: 0 36px 76px -34px rgba(0,0,0,1), 0 0 60px -26px color-mix(in srgb, var(--rp-glow, var(--rp-accent)) 55%, transparent); }
.hs-figure:hover img { transform: scale(1.035); }
.hs-figure figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 2.4rem 1.15rem .8rem;
  background: linear-gradient(transparent, rgba(6,8,13,.94));
  font-family: var(--rp-font-mono);
  font-size: .68rem; letter-spacing: .13em; text-transform: uppercase; color: var(--rp-text2);
}

/* Two-voice explainer, straight from the manual's "plain / precise" device */
.hs-voice { border-left: 2px solid var(--rp-border2); padding-left: 1.15rem; margin-bottom: 1.35rem; transition: border-color var(--rp-med); }
.hs-voice:hover { border-left-color: var(--rp-glow, var(--rp-accent)); }
.hs-voice__tag {
  display: block;
  font-family: var(--rp-font-mono);
  font-size: var(--rp-f-micro); font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--rp-glow, var(--rp-accent));
  margin-bottom: .45rem;
}
.hs-voice p { color: var(--rp-text2); }

/* Checklist */
.hs-checks { display: grid; gap: .8rem; margin-top: 1.5rem; }
.hs-checks li { display: flex; gap: .75rem; align-items: flex-start; color: var(--rp-text2); font-size: .97rem; }
.hs-checks svg { width: 19px; height: 19px; flex: none; color: var(--rp-good); margin-top: .18em; }
.hs-checks b { color: var(--rp-text); font-weight: 600; }

/* --------------------------------------------------------------------------
   12. CHARACTER KNOB ROW (Pro) -- the four colour-coded controls
   -------------------------------------------------------------------------- */
.hs-knobs { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 235px), 1fr)); gap: 1rem; }
.hs-knob {
  text-align: center;
  padding: 1.75rem 1.25rem 1.5rem;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  background: linear-gradient(168deg, var(--rp-card), var(--rp-panel));
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
}
.hs-knob:hover {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--rp-glow) 55%, transparent);
  box-shadow: 0 20px 44px -26px rgba(0,0,0,.95), 0 0 44px -22px var(--rp-glow);
}
.hs-knob__dial {
  width: 78px; height: 78px; margin: 0 auto 1.05rem;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 32%, #23252f, #0f1017 72%);
  border: 1px solid var(--rp-border2);
  position: relative;
  box-shadow: 0 5px 16px rgba(0,0,0,.55), 0 1px 0 rgba(255,255,255,.06) inset;
  transition: box-shadow var(--rp-med);
}
.hs-knob:hover .hs-knob__dial { box-shadow: 0 5px 16px rgba(0,0,0,.55), 0 0 0 3px color-mix(in srgb, var(--rp-glow) 18%, transparent); }
.hs-knob__dial::before {            /* the arc */
  content: ""; position: absolute; inset: -5px;
  border-radius: 50%;
  background: conic-gradient(from 215deg, var(--rp-glow) 0turn var(--arc, .38turn), transparent var(--arc, .38turn) 1turn);
  mask: radial-gradient(circle, transparent 61%, #000 63%, #000 72%, transparent 74%);
  -webkit-mask: radial-gradient(circle, transparent 61%, #000 63%, #000 72%, transparent 74%);
  transition: --arc var(--rp-spring);
}
.hs-knob__dial::after {             /* the pointer */
  content: ""; position: absolute;
  left: 50%; top: 15%;
  width: 2.5px; height: 24%;
  border-radius: 2px;
  background: var(--rp-glow);
  transform-origin: 50% 165%;
  transform: translateX(-50%) rotate(var(--rot, -38deg));
  box-shadow: 0 0 9px var(--rp-glow);
  transition: transform var(--rp-spring);
}
.hs-knob:hover .hs-knob__dial::after { transform: translateX(-50%) rotate(calc(var(--rot, -38deg) + 46deg)); }
.hs-knob h3 {
  font-family: var(--rp-font-mono);
  font-size: .78rem; font-weight: 700; letter-spacing: .19em; text-transform: uppercase;
  color: var(--rp-glow); margin-bottom: .3rem;
}
.hs-knob__range {
  font-family: var(--rp-font-mono);
  font-size: .66rem; letter-spacing: .1em; color: var(--rp-text3); margin-bottom: .75rem;
}
.hs-knob p { font-size: .9rem; color: var(--rp-text2); line-height: 1.58; }

/* --------------------------------------------------------------------------
   13. VIDEO
   -------------------------------------------------------------------------- */
.hs-video {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--rp-r-lg);
  overflow: hidden;
  border: 1px solid var(--rp-border2);
  background: #06080d center/cover no-repeat;
  box-shadow: 0 44px 90px -44px rgba(0,0,0,.95), 0 0 90px -34px color-mix(in srgb, var(--rp-accent) 34%, transparent);
  cursor: pointer;
}
.hs-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.hs-video__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .5; transition: opacity var(--rp-med), transform 900ms cubic-bezier(.16,.84,.44,1); }
.hs-video:hover .hs-video__poster { opacity: .62; transform: scale(1.02); }
.hs-video__play {
  position: absolute; inset: 0; display: grid; place-items: center; gap: .9rem;
  align-content: center;
}
.hs-video__disc {
  width: 84px; height: 84px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,.1);
  border: 1px solid rgba(255,255,255,.28);
  backdrop-filter: blur(9px);
  transition: transform var(--rp-spring), background var(--rp-med), box-shadow var(--rp-med);
}
.hs-video__disc svg { width: 30px; height: 30px; color: #fff; margin-left: 4px; }
.hs-video:hover .hs-video__disc {
  transform: scale(1.09);
  background: var(--rp-accent);
  box-shadow: 0 0 0 12px color-mix(in srgb, var(--rp-accent) 16%, transparent), 0 14px 40px -12px color-mix(in srgb, var(--rp-accent) 76%, transparent);
}
.hs-video__disc::before {
  content: ""; position: absolute; width: 84px; height: 84px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.34);
  animation: hs-ping 2.8s cubic-bezier(0,0,.2,1) infinite;
}
@keyframes hs-ping { 0% { transform: scale(1); opacity: .7; } 80%,100% { transform: scale(1.65); opacity: 0; } }
.hs-video__label {
  font-family: var(--rp-font-mono);
  font-size: var(--rp-f-micro); letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.82);
}

/* COMING SOON -------------------------------------------------------------
   This is the DEFAULT state in the markup. The script swaps to the play button
   only once a real video ID is present, so the page tells the truth both before
   the video exists and with JavaScript turned off. */
.hs-video__soon { position: absolute; inset: 0; display: none; place-content: center; justify-items: center; gap: .85rem; }
.hs-video--soon .hs-video__soon { display: grid; }
.hs-video--soon .hs-video__play { display: none; }
.hs-video--soon { cursor: default; }
.hs-video--soon:hover .hs-video__poster { transform: none; opacity: .5; }

.hs-video__soon-badge {
  font-family: var(--rp-font-mono);
  font-size: clamp(.85rem, 2.1vw, 1.25rem);
  font-weight: 700; letter-spacing: .3em; text-transform: uppercase;
  color: var(--rp-text);
  padding: .75em 1.5em .75em 1.8em;      /* extra left pad balances the tracking */
  border: 1px solid var(--rp-border2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--rp-bg) 72%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--rp-accent) 7%, transparent),
              0 18px 44px -20px rgba(0,0,0,.9);
  position: relative;
}
.hs-video__soon-badge::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px;
  background: linear-gradient(120deg, var(--rp-accent), transparent 42%, transparent 58%, var(--rp-brand));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: .85;
}
.hs-video__soon-note {
  font-size: var(--rp-f-small); color: var(--rp-text2); text-align: center;
  padding-inline: 1rem;
}

/* --------------------------------------------------------------------------
   14. COMPARISON TABLE
   -------------------------------------------------------------------------- */
.hs-tablewrap {
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  overflow: hidden;
  background: var(--rp-panel);
}
.hs-table { width: 100%; border-collapse: collapse; font-size: .945rem; }
.hs-table thead th {
  background: #05070c;
  padding: 1.05rem 1.15rem;
  text-align: left;
  font-family: var(--rp-font-mono);
  font-size: var(--rp-f-micro); font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--rp-text2);
  border-bottom: 1px solid var(--rp-border);
}
.hs-table thead th:nth-child(2),
.hs-table thead th:nth-child(3) { text-align: center; width: 20%; }
.hs-table thead th:nth-child(3) { color: var(--rp-brand); }
.hs-table td { padding: .92rem 1.15rem; border-bottom: 1px solid var(--rp-border); color: var(--rp-text2); vertical-align: middle; }
.hs-table td:first-child { color: var(--rp-text); font-weight: 500; }
.hs-table td:nth-child(2),
.hs-table td:nth-child(3) { text-align: center; font-family: var(--rp-font-mono); font-size: .78rem; color: var(--rp-text3); }
.hs-table tbody tr { transition: background var(--rp-fast); }
.hs-table tbody tr:hover { background: color-mix(in srgb, var(--rp-accent) 6%, transparent); }
.hs-table tbody tr:last-child td { border-bottom: 0; }
.hs-table tbody tr td:nth-child(3) { background: color-mix(in srgb, var(--rp-brand) 5%, transparent); }
.hs-yes { color: var(--rp-good); font-size: 1.15rem; line-height: 1; }
.hs-no  { color: var(--rp-text3); opacity: .55; }
@media (max-width: 720px) {
  .hs-tablewrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    /* Without this, dragging the wide comparison table sideways on a phone can
       chain into the document and fight the vertical scroll. */
    overscroll-behavior-x: contain;
  }
  .hs-table { min-width: 560px; }
}

/* --------------------------------------------------------------------------
   15. PRICING
   -------------------------------------------------------------------------- */
/* Two-value gap: <row> <column>. The row gap only ever materialises once
   auto-fit wraps the cards into a stack, so this costs nothing in the
   side-by-side layout -- there is only one row there. It has to be bigger than
   the column gap, not equal to it: stacked, the cards are full-bleed and the
   only thing separating two tiers is that gap, and at 1.25rem it was *smaller*
   than the card's own 1.7rem padding, so the boundary between two cards read as
   tighter than the whitespace inside one and the tiers looked fused. It also
   gives the Pro card's red glow somewhere to fall off before it hits Basic. */
.hs-prices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
  gap: clamp(2.25rem, 5vw, 3rem) 1.25rem;
  align-items: stretch;
}
.hs-price {
  position: relative;
  display: flex; flex-direction: column;
  padding: clamp(1.7rem, 3vw, 2.4rem);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r-lg);
  background: linear-gradient(168deg, var(--rp-card), var(--rp-panel));
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
  overflow: hidden;
}
.hs-price:hover { transform: translateY(-5px); border-color: var(--rp-border2); box-shadow: 0 30px 60px -32px rgba(0,0,0,.95); }
.hs-price--feature {
  border-color: color-mix(in srgb, var(--rp-brand) 50%, transparent);
  box-shadow: 0 0 62px -28px color-mix(in srgb, var(--rp-brand) 70%, transparent);
}
.hs-price--feature:hover { border-color: var(--rp-brand); box-shadow: 0 30px 66px -30px rgba(0,0,0,.95), 0 0 70px -24px color-mix(in srgb, var(--rp-brand) 80%, transparent); }
.hs-price--feature::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--rp-brand), transparent);
}
.hs-price__flag {
  position: absolute; top: 1.15rem; right: 1.15rem;
  padding: .3em .75em; border-radius: 999px;
  background: var(--rp-brand); color: #fff;
  font-family: var(--rp-font-mono);
  font-size: .62rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
}
.hs-price__tier {
  font-family: var(--rp-font-mono);
  font-size: .74rem; font-weight: 700; letter-spacing: .2em; text-transform: uppercase;
  color: var(--rp-text3); margin-bottom: .85rem;
}
.hs-price__amt { display: flex; align-items: baseline; gap: .4rem; margin-bottom: .35rem; }
.hs-price__amt b {
  font-size: clamp(2.6rem, 5vw, 3.5rem); font-weight: 700; line-height: 1; letter-spacing: -.04em;
  font-family: var(--rp-font-display);
}
.hs-price__amt span { font-size: .9rem; color: var(--rp-text3); }
.hs-price__sub { color: var(--rp-text2); font-size: .93rem; margin-bottom: 1.5rem; min-height: 2.6em; }
.hs-price__list { display: grid; gap: .68rem; margin-bottom: 1.85rem; flex: 1; }
.hs-price__list li { display: flex; gap: .68rem; align-items: flex-start; font-size: .92rem; color: var(--rp-text2); }
.hs-price__list svg { width: 17px; height: 17px; flex: none; margin-top: .2em; }
.hs-price__list li b { color: var(--rp-text); font-weight: 600; }
.hs-price__note { margin-top: .95rem; text-align: center; font-size: .78rem; color: var(--rp-text3); }
/* One tier must not stretch the full grid - constrain and centre it so a
   single card reads as deliberate rather than as a layout that lost its
   sibling. Must stay AFTER .hs-prices above: same specificity, later wins. */
.hs-prices--single { grid-template-columns: minmax(0, 420px); justify-content: center; }
/* A tier with no ls_product_id yet - not-yet-purchasable, not a dead link. */
.hs-btn--disabled { opacity: .55; cursor: default; pointer-events: none; }
.hs-btn--disabled::after { display: none; }
/* A trial that shares its tier's checkout sits under the buy button as the
   quieter of the two routes into the same product. */
.hs-btn--trial { margin-top: .6rem; }
.hs-price__note--trial { margin-top: .5rem; }

/* --------------------------------------------------------------------------
   16. TESTIMONIALS
   -------------------------------------------------------------------------- */
.hs-quotes { columns: 3 300px; column-gap: 1.15rem; }
.hs-quote {
  break-inside: avoid; margin-bottom: 1.15rem;
  padding: 1.55rem;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  background: linear-gradient(168deg, var(--rp-card), var(--rp-panel));
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
}
.hs-quote:hover { transform: translateY(-3px); border-color: var(--rp-border2); box-shadow: 0 20px 44px -26px rgba(0,0,0,.9); }
.hs-quote__stars { display: flex; gap: 2px; margin-bottom: .9rem; color: var(--rp-warn); }
.hs-quote__stars svg { width: 15px; height: 15px; }
.hs-quote p { color: var(--rp-text); font-size: .975rem; line-height: 1.65; margin-bottom: 1.1rem; }
.hs-quote footer { display: flex; align-items: center; gap: .7rem; }
.hs-quote__av {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: linear-gradient(150deg, var(--rp-accent), var(--rp-brand));
  color: #04060c; font-weight: 700; font-size: .84rem;
}
.hs-quote__who b { display: block; font-size: .89rem; font-weight: 600; }
.hs-quote__who span { display: block; font-size: .77rem; color: var(--rp-text3); }
/* Optional per-entry portrait. A testimonial without an image renders exactly
   as it always did; only an entry carrying image: gets these. */
.hs-quote--portrait { padding: 0; overflow: hidden; }
.hs-quote--portrait > :not(.hs-quote__img) { padding-inline: 1.55rem; }
.hs-quote--portrait > :first-child + * { padding-top: 1.4rem; }
.hs-quote--portrait footer { padding-bottom: 1.55rem; }
.hs-quote__img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 16 / 9; object-fit: cover;
  border-bottom: 1px solid var(--rp-border);
}

/* --------------------------------------------------------------------------
   16b. REVIEWS -- a wall of video cards. Each is a plain link over a locally
   served thumbnail, so a page carrying this section still makes no request to
   any video host until a visitor clicks through.
   -------------------------------------------------------------------------- */
.hs-reviews { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
@media (max-width: 720px) { .hs-reviews { grid-template-columns: minmax(0, 1fr); } }
.hs-review {
  display: block; text-decoration: none; color: inherit;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  background: linear-gradient(168deg, var(--rp-card), var(--rp-panel));
  overflow: hidden;
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
}
.hs-review:hover { transform: translateY(-3px); border-color: var(--rp-border2); box-shadow: 0 20px 44px -26px rgba(0,0,0,.9); }
.hs-review__thumb { display: block; position: relative; aspect-ratio: 16 / 9; background: #000; }
.hs-review__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.hs-review__play {
  position: absolute; inset: 0; margin: auto;
  width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(8,10,16,.72); border: 1px solid rgba(255,255,255,.22);
  color: #fff; backdrop-filter: blur(2px);
  transition: transform var(--rp-med), background var(--rp-med);
}
.hs-review__play svg { width: 26px; height: 26px; margin-left: 3px; }
.hs-review:hover .hs-review__play { transform: scale(1.08); background: var(--rp-brand); color: var(--rp-on-accent); }
.hs-review__flag {
  position: absolute; top: .7rem; left: .7rem;
  font-family: var(--rp-font-mono);
  font-size: .6rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--rp-text2); background: rgba(8,10,16,.82);
  border: 1px solid var(--rp-border2); border-radius: 999px;
  padding: .3em .7em;
}
.hs-review__meta { display: block; padding: 1.05rem 1.2rem 1.2rem; }
.hs-review__title { display: block; font-size: .95rem; font-weight: 600; line-height: 1.4; color: var(--rp-text); }
.hs-review__channel { display: block; margin-top: .35rem; font-size: .8rem; color: var(--rp-text3); }

/* --------------------------------------------------------------------------
   17. COMPATIBILITY
   -------------------------------------------------------------------------- */
.hs-os {
  padding: clamp(1.5rem, 2.6vw, 2rem);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  background: var(--rp-panel);
  transition: border-color var(--rp-med);
}
.hs-os:hover { border-color: var(--rp-border2); }
.hs-os__head { display: flex; align-items: center; gap: .75rem; margin-bottom: 1.25rem; padding-bottom: 1.1rem; border-bottom: 1px solid var(--rp-border); }
.hs-os__head svg { width: 26px; height: 26px; color: var(--rp-text2); }
.hs-os__head h3 { font-size: 1.06rem; }
.hs-os dl { display: grid; grid-template-columns: auto 1fr; gap: .7rem 1.15rem; font-size: .9rem; }
.hs-os dt { font-family: var(--rp-font-mono); font-size: .68rem; letter-spacing: .13em; text-transform: uppercase; color: var(--rp-text3); padding-top: .2em; }
.hs-os dd { color: var(--rp-text2); margin: 0; }

.hs-formats { display: flex; flex-wrap: wrap; gap: .45rem; }
.hs-format {
  padding: .34em .8em; border-radius: 7px;
  border: 1px solid var(--rp-border2);
  background: rgba(255,255,255,.028);
  font-family: var(--rp-font-mono);
  font-size: .7rem; font-weight: 700; letter-spacing: .1em; color: var(--rp-text2);
  transition: all var(--rp-fast);
}
.hs-format:hover { border-color: var(--rp-accent); color: var(--rp-accent); background: color-mix(in srgb, var(--rp-accent) 10%, transparent); transform: translateY(-1px); }

/* --------------------------------------------------------------------------
   18. FAQ -- the accordion animates grid-template-rows 0fr->1fr, which is the
   only pure-CSS way to transition to "auto" height. No JS height math.
   -------------------------------------------------------------------------- */
.hs-faq { display: grid; gap: .7rem; }
.hs-faq__item {
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r-sm);
  background: var(--rp-panel);
  overflow: hidden;
  transition: border-color var(--rp-med), background var(--rp-med);
}
.hs-faq__item:hover { border-color: var(--rp-border2); }
.hs-faq__item.is-open { border-color: color-mix(in srgb, var(--rp-accent) 42%, transparent); background: var(--rp-card); }
.hs-faq__q {
  width: 100%; display: flex; align-items: center; gap: 1rem;
  padding: 1.15rem 1.35rem;
  text-align: left; font-size: 1.005rem; font-weight: 600; line-height: 1.4;
  transition: color var(--rp-fast);
}
.hs-faq__item.is-open .hs-faq__q { color: var(--rp-accent); }
.hs-faq__sign { margin-left: auto; flex: none; width: 17px; height: 17px; position: relative; }
.hs-faq__sign::before,
.hs-faq__sign::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: currentColor; border-radius: 2px;
  transform: translate(-50%, -50%);
  transition: transform var(--rp-spring), opacity var(--rp-fast);
}
.hs-faq__sign::before { width: 15px; height: 2px; }
.hs-faq__sign::after  { width: 2px; height: 15px; }
.hs-faq__item.is-open .hs-faq__sign::after { transform: translate(-50%,-50%) rotate(90deg); opacity: 0; }
.hs-faq__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--rp-med); }
.hs-faq__item.is-open .hs-faq__panel { grid-template-rows: 1fr; }
.hs-faq__panel > div { overflow: hidden; }
.hs-faq__panel p { padding: 0 1.35rem 1.3rem; color: var(--rp-text2); font-size: .955rem; }
.hs-faq__panel p + p { padding-top: 0; }

/* --------------------------------------------------------------------------
   19. CONTACT
   -------------------------------------------------------------------------- */
.hs-contact { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 1.15rem; }
.hs-contact__card {
  display: flex; flex-direction: column; align-items: flex-start; gap: .55rem;
  padding: 1.65rem;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-r);
  background: linear-gradient(168deg, var(--rp-card), var(--rp-panel));
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
}
.hs-contact__card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--rp-glow, var(--rp-accent)) 50%, transparent);
  box-shadow: 0 22px 46px -28px rgba(0,0,0,.92), 0 0 44px -26px var(--rp-glow, var(--rp-accent));
}
.hs-contact__ico {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--rp-glow, var(--rp-accent)) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--rp-glow, var(--rp-accent)) 30%, transparent);
  color: var(--rp-glow, var(--rp-accent));
  margin-bottom: .5rem;
  transition: transform var(--rp-spring);
}
.hs-contact__card:hover .hs-contact__ico { transform: scale(1.07) rotate(-5deg); }
.hs-contact__ico svg { width: 21px; height: 21px; }
.hs-contact__card h3 { font-size: 1.03rem; }
.hs-contact__card p { font-size: .91rem; color: var(--rp-text2); }
.hs-contact__link {
  margin-top: auto; padding-top: .55rem;
  font-family: var(--rp-font-mono);
  font-size: .8rem; color: var(--rp-glow, var(--rp-accent));
  display: inline-flex; align-items: center; gap: .4em;
  transition: gap var(--rp-fast);
}
.hs-contact__card:hover .hs-contact__link { gap: .7em; }

/* --------------------------------------------------------------------------
   20. FINAL CTA + FOOTER
   -------------------------------------------------------------------------- */
.hs-final {
  position: relative; overflow: hidden;
  border-radius: var(--rp-r-lg);
  border: 1px solid var(--rp-border2);
  background: linear-gradient(150deg, #12131c, #0a0b11 55%, #150d12);
  padding: clamp(2.8rem, 6vw, 5rem) clamp(1.5rem, 4vw, 3.5rem);
  /* NB: centring comes from the .hs-center class on the element, not from a
     text-align here. Setting it locally is what caused the button row to stay
     left-aligned: text-align centres inline content, but .hs-cta-row is a flex
     container and needs justify-content -- which only .hs-center supplies. One
     mechanism, one place. */
}
.hs-final::before {
  content: ""; position: absolute; inset: -50% -10%;
  background:
    radial-gradient(38% 55% at 18% 50%, color-mix(in srgb, var(--rp-accent) 32%, transparent), transparent 70%),
    radial-gradient(38% 55% at 82% 50%, color-mix(in srgb, var(--rp-brand) 30%, transparent), transparent 70%);
  filter: blur(48px); pointer-events: none;
  animation: hs-drift 18s ease-in-out infinite alternate;
}
@keyframes hs-drift { from { transform: translateX(-3%); } to { transform: translateX(3%); } }
.hs-final > * { position: relative; z-index: 1; }

.hs-foot { border-top: 1px solid var(--rp-border); padding-block: 2.6rem 3rem; }
.hs-foot__inner { display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem; align-items: center; }
.hs-foot__logo { width: 132px; }
.hs-foot__logo svg { width: 100%; height: auto; opacity: .85; }
.hs-foot__links { display: flex; flex-wrap: wrap; gap: 1.35rem; margin-left: auto; }
.hs-foot__links a { font-size: .855rem; color: var(--rp-text3); transition: color var(--rp-fast); }
.hs-foot__links a:hover { color: var(--rp-accent); }
.hs-foot__legal { width: 100%; font-size: .78rem; color: var(--rp-text3); padding-top: 1.4rem; border-top: 1px solid var(--rp-border); }

/* --------------------------------------------------------------------------
   21. SCROLL REVEAL -- the class is added by IntersectionObserver.
   Everything starts hidden ONLY if JS is running (html.hs-js), so the page
   is still fully readable with JS blocked. That's the progressive-enhancement
   rule: never hide content you can't guarantee you'll show again.
   -------------------------------------------------------------------------- */
.hs-js .hs-reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--rp-slow), transform var(--rp-slow);
  transition-delay: var(--d, 0ms);
  will-change: opacity, transform;
}
.hs-js .hs-reveal.is-in { opacity: 1; transform: none; }

/* ------------------------------------------------- customer reviews --- */
/* Score beside the breakdown rather than above it: same information, about
   half the height, and the bars get the width to actually be read. */
.hs-rsummary__eyebrow { display: block; text-align: center; margin-bottom: 1.1rem; }
.hs-rsummary {
  display: grid; grid-template-columns: minmax(0, 210px) minmax(0, 1fr);
  gap: clamp(1.4rem, 3.5vw, 3rem); align-items: center;
  margin-bottom: clamp(1.8rem, 3.5vw, 2.6rem);
}
.hs-rsummary__score { text-align: center; }
.hs-rsummary__num {
  font-size: clamp(2.4rem, 5vw, 3.4rem); font-weight: 700;
  line-height: 1; letter-spacing: -.03em; margin: .7rem 0 .5rem;
}
.hs-rsummary__count {
  margin: .55rem 0 0; font-family: var(--rp-font-mono); font-size: .66rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--rp-text3);
}
@media (max-width: 760px) {
  .hs-rsummary { grid-template-columns: minmax(0, 1fr); }
  .hs-rbars { max-width: 320px; margin-inline: auto; }
}

/* The breakdown. Every row is counted from the ratings, never written by hand. */
.hs-rbars { display: grid; gap: .3rem; }
.hs-rbar {
  display: grid; grid-template-columns: 2.4ch minmax(0, 1fr) 2.5ch;
  gap: .6rem; align-items: center;
  font-family: var(--rp-font-mono); font-size: .64rem; color: var(--rp-text3);
}
.hs-rbar__track {
  height: 7px; border-radius: 999px; overflow: hidden;
  background: rgba(255, 255, 255, .09);
}
.hs-rbar__fill {
  display: block; height: 100%; border-radius: 999px;
  background: var(--rp-accent);
}
.hs-rbar__c { text-align: right; }

/* Cards. Taller than a clamped teaser so a short review is whole on the card,
   and long ones fade rather than cut mid-word. */
.hs-creviews {
  display: grid; gap: clamp(.7rem, 1.6vw, 1rem);
  /* Four, explicitly. auto-fit picked three at this measure and stranded the
     fourth card alone on a second row. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 940px) { .hs-creviews { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Still two across on a phone - the pager drops to 2 per page to match, so a
   card keeps a readable width instead of being squeezed to a column of four.
   Everything shrinks a step rather than the layout collapsing. */
@media (max-width: 520px) {
  .hs-creviews { gap: .55rem; }
  .hs-creview { min-height: 210px; padding: .8rem .85rem; gap: .4rem; }
  .hs-creview__title { font-size: .82rem; }
  .hs-creview__body { font-size: .78rem; -webkit-line-clamp: 5; }
  .hs-creview__by { font-size: .54rem; gap: .3rem; flex-wrap: wrap; }
  .hs-creview__more { display: none; }
}
.hs-creview {
  position: relative; overflow: hidden;
  min-height: 250px; padding: 1rem 1.1rem;
  display: flex; flex-direction: column; gap: .5rem; text-align: left;
  border: 1px solid var(--rp-border); border-radius: var(--rp-r-lg);
  background: linear-gradient(168deg, var(--rp-card, #12131c), var(--rp-panel, #0d0e15));
  color: inherit; font: inherit; cursor: pointer;
  transition: border-color var(--rp-med), transform var(--rp-med), box-shadow var(--rp-med);
}
.hs-root .hs-creview:hover, .hs-root .hs-creview:focus-visible {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--rp-accent) 55%, transparent);
  box-shadow: 0 24px 46px -30px rgba(0,0,0,.95);
}
.hs-creview.is-hidden { display: none; }
.hs-creview__title { font-size: .92rem; color: var(--rp-text); }
.hs-creview__body {
  font-size: .85rem; line-height: 1.55; color: var(--rp-text2);
  display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical;
  overflow: hidden;
}
.hs-creview__by {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .6rem; margin-top: auto; padding-top: .5rem;
  font-family: var(--rp-font-mono); font-size: .6rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--rp-text3);
}
.hs-creview__name { color: var(--rp-text2); font-weight: 700; }
/* Quiet on purpose. The badge is a fact, not a sales device - it sits beside
   the name at the same weight as the date, and never competes with the review
   itself. Green rather than the product accent, so it reads as a status and
   not as decoration. */
/* Rating and badge on one line: both answer "how much does this count", so
   they are read together, above the words rather than buried under them. Wraps
   rather than squeezing - on the narrowest cards the badge drops to its own
   line instead of crushing the stars. */
.hs-creview__top {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: .4rem .5rem;
}
.hs-vbadge {
  display: inline-flex; align-items: center; gap: .25em;
  padding: .15em .5em;
  font-family: var(--rp-font-mono); font-size: .54rem; text-transform: uppercase;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, #35d07f 45%, transparent);
  background: color-mix(in srgb, #35d07f 12%, transparent);
  color: #7ce0ab;
  font-weight: 700; letter-spacing: .08em;
  white-space: nowrap;
}
.hs-rmodal__verified {
  display: inline-flex; align-items: center; gap: .3em;
  margin: .8rem 0 0; padding: .25em .7em;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, #35d07f 45%, transparent);
  background: color-mix(in srgb, #35d07f 12%, transparent);
  color: #7ce0ab;
  font-family: var(--rp-font-mono); font-size: .62rem;
  letter-spacing: .12em; text-transform: uppercase;
}
.hs-rmodal__verified[hidden] { display: none; }
/* Present for a mouse only on hover, but always in the accessibility tree via
   the button role - a keyboard user gets it from focus styling instead. */
.hs-creview__more {
  font-family: var(--rp-font-mono); font-size: .56rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--rp-accent);
  opacity: 0; transition: opacity var(--rp-med);
}
.hs-creview:hover .hs-creview__more,
.hs-creview:focus-visible .hs-creview__more { opacity: 1; }

/* Paging controls. Same shape as the hero slider's arrows so the two read as
   the same control, with a count between them - without it there is no way to
   tell how much is left, and an arrow that silently wraps feels broken. */
.hs-pager {
  display: flex; align-items: center; justify-content: center; gap: .9rem;
  margin-top: clamp(1.1rem, 2.4vw, 1.6rem);
}
.hs-pager__arrow {
  display: grid; place-items: center;
  width: 38px; height: 38px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(255, 255, 255, .06);
  color: var(--rp-text);
  transition: background var(--rp-med), border-color var(--rp-med), transform var(--rp-med);
}
.hs-root .hs-pager__arrow:hover {
  background: rgba(255, 255, 255, .14);
  border-color: color-mix(in srgb, var(--rp-accent) 70%, transparent);
  transform: scale(1.06);
}
.hs-pager__arrow:focus-visible { outline: 2px solid var(--rp-accent); outline-offset: 3px; }
.hs-pager__arrow svg { width: 16px; height: 16px; }
.hs-pager__count {
  min-width: 5ch; text-align: center;
  font-family: var(--rp-font-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .12em; color: var(--rp-text3);
}
@media (prefers-reduced-motion: reduce) {
  .hs-pager__arrow { transition: none; }
  .hs-root .hs-pager__arrow:hover { transform: none; }
}

/* The full review. <dialog> supplies the backdrop and Escape; this only has to
   look like the rest of the site. */
.hs-rmodal {
  width: min(92vw, 640px); padding: clamp(1.5rem, 3vw, 2.2rem);
  border: 1px solid var(--rp-border2); border-radius: var(--rp-r-lg);
  background: var(--rp-panel, #0d0e15); color: var(--rp-text);
  box-shadow: 0 40px 90px -40px rgba(0,0,0,1);
}
.hs-rmodal::backdrop { background: rgba(0, 0, 0, .72); backdrop-filter: blur(3px); }
.hs-rmodal__close {
  position: absolute; top: .7rem; right: .8rem;
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--rp-border); background: none;
  color: var(--rp-text3); font-size: 1.2rem; line-height: 1;
}
.hs-root .hs-rmodal__close:hover { color: var(--rp-text); }
.hs-rmodal__title { display: block; margin: .8rem 0 .6rem; font-size: 1.15rem; }
.hs-rmodal__body { margin: 0; color: var(--rp-text2); line-height: 1.7; }
.hs-rmodal__by {
  margin: 1.1rem 0 0; font-family: var(--rp-font-mono); font-size: .66rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--rp-text3);
}

/* Stars. An outline for the unearned ones rather than a missing shape, so a
   5-star review can never be mistaken for a 1-star one. */
.hs-stars { display: inline-flex; gap: .12rem; color: var(--rp-brand); }
.hs-stars svg { width: 15px; height: 15px; }
.hs-stars--lg { justify-content: center; }
.hs-stars--lg svg { width: 22px; height: 22px; }

@media (prefers-reduced-motion: reduce) {
  .hs-creview, .hs-creviews__all, .hs-creview__more { transition: none; }
  .hs-root .hs-creview:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   22. REDUCED MOTION -- respect the OS setting. Non-negotiable for a11y.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .hs-root *,
  .hs-root *::before,
  .hs-root *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .hs-js .hs-reveal { opacity: 1 !important; transform: none !important; }
  .hs-shot__frame { transform: none !important; }
}

/* ----------------------------------------------------------- hero slider --- */
/* Only present when a product declares hero.images. Every slide is stacked in
   the same box and cross-faded, so the frame never changes size between shots
   - the images are all the same aspect, and this keeps the page from jumping
   even if one day they are not.

   The first slide keeps its place in flow and sets the height; the rest are
   absolutely positioned over it. Without JavaScript they simply stack and the
   first one is on top, so the hero still shows the plugin. */
/* pan-y: vertical scrolling stays with the browser, horizontal gestures
   come to the slider. Without it a sideways drag scrolls the page and the
   swipe never lands. */
.hs-shot--slider .hs-shot__frame { position: relative; touch-action: pan-y; }
.hs-shot__slide { display: block; width: 100%; }
.hs-shot--slider .hs-shot__slide + .hs-shot__slide {
  position: absolute; inset: 0; height: 100%;
}
/* .hs-js is set by the page script, so these opacity rules only take effect
   once something can change them back. */
.hs-js .hs-shot--slider .hs-shot__slide {
  opacity: 0; transition: opacity 620ms ease;
}
.hs-js .hs-shot--slider .hs-shot__slide.is-current { opacity: 1; }

.hs-shot__dots {
  display: flex; justify-content: center; align-items: center; gap: .5rem;
  margin-top: 1rem;
}
/* NOT --rp-text3 for the inactive state. A product theme may repaint that
   token - Clarity Delay sets it to the guide's teal - which made the inactive
   dot the SAME colour as the active one, so nothing indicated which slide you
   were on. A fixed white at low alpha reads on any product's ground. */
.hs-root .hs-shot__dot {
  width: 30px; height: 6px; padding: 0; border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, .26);
  cursor: pointer;
  transition: background var(--rp-med), width var(--rp-med), box-shadow var(--rp-med);
}
.hs-root .hs-root .hs-shot__dot:hover { background: rgba(255, 255, 255, .55); }
/* The current slide is wider AND brighter: two signals, so it still reads for
   anyone who cannot separate the accent from white. */
.hs-root .hs-shot__dot[aria-current="true"] {
  width: 46px;
  background: var(--rp-accent);
  box-shadow: 0 0 14px -2px color-mix(in srgb, var(--rp-accent) 70%, transparent);
}
.hs-root .hs-root .hs-shot__dot:focus-visible { outline: 2px solid var(--rp-accent); outline-offset: 3px; }

/* Prev/next. Swipe already works on touch; these are the same control for a
   mouse, which has nothing to swipe with. */
.hs-root .hs-shot__arrow {
  display: grid; place-items: center;
  width: 34px; height: 34px; padding: 0;
  border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(255, 255, 255, .06);
  color: var(--rp-text);
  transition: background var(--rp-med), border-color var(--rp-med), transform var(--rp-med);
}
.hs-root .hs-shot__arrow:hover {
  background: rgba(255, 255, 255, .14);
  border-color: color-mix(in srgb, var(--rp-accent) 70%, transparent);
  transform: scale(1.06);
}
.hs-root .hs-shot__arrow:focus-visible { outline: 2px solid var(--rp-accent); outline-offset: 3px; }
.hs-shot__arrow svg { width: 15px; height: 15px; }
@media (prefers-reduced-motion: reduce) {
  .hs-shot__dot, .hs-shot__arrow { transition: none; }
  .hs-shot__arrow:hover { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hs-js .hs-shot--slider .hs-shot__slide { transition: none; }
}

/* --------------------------------------------------- review submission --- */
.hs-reviewform {
  display: grid; gap: .7rem; max-width: 620px; margin: 0 auto;
  grid-template-columns: 1fr 1fr;
}
.hs-reviewform__body, .hs-reviewform__go,
.hs-reviewform__note { grid-column: 1 / -1; }
.hs-reviewform__field {
  padding: .85rem 1.1rem; border-radius: var(--rp-r);
  border: 1px solid var(--rp-border2); background: rgba(255,255,255,.03);
  color: var(--rp-text); font: inherit;
}
/* Dark the whole control, popup list included. Without color-scheme the
   option list is drawn by the UA in its light palette, which put near-white
   text on a white background - legible only by luck. */
.hs-reviewform__field { color-scheme: dark; }
.hs-reviewform__field option {
  background: var(--rp-panel, #0d0e15);
  color: var(--rp-text, #eceef6);
}
.hs-reviewform__field:focus {
  outline: 2px solid var(--rp-accent); outline-offset: 2px; border-color: transparent;
}
.hs-reviewform__note {
  margin: .2rem 0 0; min-height: 1.4em; font-size: .9rem; color: var(--rp-text2);
}
@media (max-width: 560px) { .hs-reviewform { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   HUB -- the plugin index at the site root.

   Thin on purpose: it reuses tier-1 tokens and base.css for type, buttons and
   the reveal animation, and adds only what has no equivalent on a product
   page. Each card is tinted by its own product's tier-2 palette, which
   build.py inlines re-scoped to .hub-card--<slug>, so a product's signature
   colour reaches the index without this file knowing any product's name.
   -------------------------------------------------------------------------- */
.hs-root {
  /* HOUSE ACCENT - Ultraviolet. Deliberately not either product's colour:
     Clarity owns #ab8cff and #24f1e0, HardStop owns #ff5765 and #3aa6ff. An
     accent that collided would make the whole site look like that product's
     site. This sits deeper than Clarity's lilac so the two never read as the
     same purple. */
  --hub-accent: #7c5cff;
  --hub-ink:    #f3efff;
  /* The index's own faint step. NOT --rp-text3: a product theme may repaint
     that token for its own page - Clarity Delay sets it to the guide's teal so
     credit lines and price notes read as an accent - and those themes are
     re-scoped into the cards here, which turned one card's neutral CTA teal
     while the other stayed grey. This one belongs to the hub, so every card
     agrees regardless of what a product does to its own palette. */
  --hub-muted:  #5e617a;
  /* Headlines are painted with a cool platinum ramp clipped to the glyphs -
     the logo's own material, so the type reads as milled rather than printed
     beside it. Swap this one value for a flat colour to turn the treatment
     off everywhere. */
  --hub-metal: linear-gradient(176deg, #ffffff 0%, #dfe6ec 26%, #8b949e 52%,
                               #f4f7f9 62%, #aab3bc 80%, #767f88 100%);
}

/* The UA's default body margin (8px) was never reset. It is invisible on a
   product page, where everything sits inside a max-width wrap, but a
   full-bleed hero cannot reach the viewport edge with it in place - the image
   rendered 1409px wide in a 1425px viewport. Scoped here rather than in
   base.css, which the byte-locked Divi target inlines. */
html, body { margin: 0; }

/* The hub's ground is TRUE BLACK, matching the hero artwork's own
   background exactly (#000000, measured at all four edges and the midpoints).
   Scoped to the index - the shared token stays #0a0b11, so every product page
   keeps the slightly-blue near-black the design system was built on. Matching
   is what lets the hero run full width with no visible edge. */
.hs-root { background: #000; }

/* No bottom padding here: the gap under the strapline is owned entirely by
   the section below it. Splitting that space between two elements is what
   made the text sit closer to the artwork than to the deal bar. */
.hub-head { padding: 0; text-align: center; }

/* Sized by BOTH axes, so the whole lockup is on screen at once.

   Width alone was not enough: the artwork is 16:9, so at full viewport width
   it stands taller than the window and the "premium plugins that inspire"
   line fell below the fold - you had to scroll to see the piece you were
   meant to see first.

   width:auto with two maximums lets the browser scale it uniformly against
   whichever limit bites first: max-width on a narrow window, max-height on a
   wide or short one. Aspect ratio is preserved either way - nothing is
   cropped or squashed.

   78svh, not 100: the small-viewport unit accounts for mobile browser chrome
   that vh gets wrong, and leaving a quarter of the fold below the artwork
   means the lede peeks in as a cue that the page continues.

   No mask - page ground and artwork ground are both #000000, so a full-width
   image and a centred one look identical at the edges. */
/* FULL BLEED, edge to edge. The artwork is 2:1 with its sides faded into
   black, so it is meant to run the whole width rather than sit as a centred
   picture with margins.

   A definite height plus object-fit is what allows the crop: at wide viewports
   the natural height (100vw / 2) is taller than the cap, and the overflow is
   taken off the TOP - object-position keeps the lower band, where the lockup
   and "Mix faster. Stay inspired." sit. Where the natural height fits under
   the cap, min() picks it and nothing is cropped at all. */
.hub-hero {
  display: block;
  /* NO CROP. Intrinsic sizing bounded on both axes: the image is as wide as
     its own height allows, and nothing is cut off the top.

     At 2:1 the two goals fight each other - running edge to edge on a 1600px
     window would make it 800px tall and push the deal bar off the screen
     again. So the height cap wins and the width follows from it. The sides
     fade to black and the page ground is black, so the space either side
     reads as part of the artwork rather than as margin. */
  width: auto;
  height: auto;
  max-width: 100%;
  /* 62, not 66: at 66 the deal bar cleared the fold on a 830px window but
     missed it by 7px on a 770px one. This holds on both. */
  max-height: 62vh;
  max-height: 62svh;
  margin: 0 auto;
}
@media (max-width: 700px) {
  /* A phone is taller than it is wide: full width is reachable there, so let
     the picture take it. */
  .hub-hero { max-height: 46svh; }
}

/* The strapline. Montserrat Light at wide tracking, echoing the lockup in the
   artwork above it without competing: the picture shouts, this states. */
.hub-strap {
  margin: clamp(1rem, 2.4vw, 1.7rem) auto 0;
  font-family: "Montserrat", var(--rp-font-display), system-ui, sans-serif;
  font-weight: 300;
  /* Up ~3pt from the first pass - it was reading as a caption rather than a
     line of the lockup. */
  font-size: clamp(.95rem, 1.75vw, 1.3rem);
  letter-spacing: .26em;
  text-transform: uppercase;
  line-height: 1.7;
  color: var(--rp-text2);
  max-width: 46ch;
  /* The last word must not strand on its own line at the tracking above. */
  text-wrap: balance;
}
/* The second line carries the claim, so it carries the weight. Same family and
   tracking - only the weight changes, which reads as emphasis rather than as a
   different piece of text. */
.hub-strap__emph { font-weight: 500; color: var(--rp-text); white-space: nowrap; }

/* The first section rides up under the hero. Its default top padding is set
   for sections that follow other sections; here it only pushed the deal bar
   and its countdown off the first screen. The spacing between the strapline
   and the bar is the header's own bottom padding, which is enough. */
/* The SAME clamp the strapline uses for its own top margin, so the two gaps
   stay equal at every viewport width, not just the one they were measured on. */
#hubPlugins { padding-top: clamp(1rem, 2.4vw, 1.7rem); }
@media (max-width: 520px) {
  .hub-strap { letter-spacing: .2em; }
  .hub-strap__emph { white-space: normal; }
}

/* ---------------------------------------------------------------- deals --- */
.hub-deal {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .75rem 1.1rem;
  margin: 0 auto clamp(1.6rem, 3vw, 2.4rem);
  padding: .7rem 1.15rem; width: fit-content; max-width: 100%;
  /* Not a pill: the countdown takes its own line below the price. */
  border-radius: var(--rp-r-lg);
  border: 1px solid color-mix(in srgb, var(--hub-accent) 45%, transparent);
  background: color-mix(in srgb, var(--hub-accent) 10%, transparent);
}
.hub-deal__tag {
  font-family: var(--rp-font-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--hub-ink); background: var(--hub-accent);
  border-radius: 999px; padding: .4em .85em;
}
.hub-deal__price { font-size: 1rem; color: var(--rp-text); }
.hub-deal__price s {
  color: var(--rp-text2); margin-right: .15em; text-decoration-thickness: .08em;
}
.hub-deal__price b { color: var(--hub-accent); }
/* OWN LINE - see .hs-sale__clock, same reasoning. */
.hub-deal__clock {
  flex-basis: 100%; justify-content: center;
  padding-top: .62rem;
  border-top: 1px solid color-mix(in srgb, var(--hub-accent) 26%, transparent);
  display: inline-flex; align-items: baseline; gap: .5em;
  font-family: var(--rp-font-mono);
}
.hub-deal__clock-label { font-size: .72rem; letter-spacing: .13em; text-transform: uppercase; color: var(--rp-text3); }
/* Tabular figures so ticking seconds do not shift the row. */
.hub-deal__clock-val { font-size: 1.02rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------- line heads --- */
/* One per product line, and the loudest text on the page after the hero
   lockup - these two headings ARE the index's structure, so they are sized to
   be read at a glance from a scroll rather than found by looking. The rule
   under the name divides the page the way a heading in print does. */
.hub-line {
  margin: clamp(3rem, 6vw, 4.6rem) 0 clamp(1.4rem, 2.4vw, 2rem);
  border-bottom: 1px solid var(--rp-border);
  padding-bottom: clamp(.9rem, 1.6vw, 1.25rem);
  text-align: center;
}
.hub-line:first-child { margin-top: 0; }
.hub-line__name {
  margin: 0;
  /* Tracking OPENS as the caps get bigger rather than tightening: set solid,
     an all-caps line at this size closes up and reads as one long word. */
  font-size: clamp(2rem, 4.6vw, 3.05rem); font-weight: 800;
  line-height: 1.04; letter-spacing: .04em; text-transform: uppercase;
  color: var(--rp-text);
}
/* The subheadline. Centred WITH the heading and auto-margined so the measure
   stays readable - a centred headline over a left-flush paragraph reads as a
   mistake. Set above body size: it is the second thing read, not a caption. */
.hub-line__sub {
  margin: .8rem auto 0; max-width: 54ch;
  font-size: clamp(1.02rem, 1.5vw, 1.2rem); line-height: 1.5;
  color: var(--rp-text2); text-wrap: balance;
}

/* Who licenses it and who answers email, under the grid. Small caps rather
   than the mono uppercase used for buttons: this is a sentence to be read,
   not a control to be clicked, and small caps keep it quiet without losing
   the word shapes that make a sentence scannable. */
.hub-line__note {
  margin: clamp(1.5rem, 2.6vw, 2.1rem) auto 0; max-width: 60ch;
  text-align: center;
  font-variant-caps: all-small-caps; font-feature-settings: "c2sc", "smcp";
  font-size: .95rem; font-weight: 600; letter-spacing: .07em; line-height: 1.5;
  color: var(--rp-text3);
}

/* A line with nothing in it yet. Dashed and centred so it cannot be mistaken
   for a card at a glance - it is a gap the shop has not filled, and it should
   look like one. Kept at the grid's own rhythm so the page does not collapse
   where a row of cards will later sit. */
.hub-line__soon {
  margin: 0; padding: clamp(2.4rem, 5vw, 3.6rem) 1.25rem; text-align: center;
  border: 1px dashed var(--rp-border); border-radius: var(--rp-r-lg);
  font-family: var(--rp-font-mono); font-size: .82rem; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase; color: var(--rp-text3);
}

/* ----------------------------------------------------------------- grid --- */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
  gap: clamp(1.25rem, 2.5vw, 1.9rem);
}
/* A grid holding ONE card would stretch it across the full row - auto-fit
   collapses the empty track - and a lone free plugin would render at twice the
   size of a flagship one. Capped to roughly the width a card gets in a two-up
   row, so every card on the page reads at the same scale. min() rather than a
   media query: on a narrow screen 100% wins and the card fills the column. */
.hub-grid > .hub-card:only-child { max-width: min(100%, 560px); margin-inline: auto; }

.hub-card {
  display: flex; flex-direction: column;
  text-decoration: none; color: inherit;
  border: 1px solid var(--rp-border); border-radius: var(--rp-r-lg);
  background: linear-gradient(168deg, var(--rp-card), var(--rp-panel));
  overflow: hidden;
  transition: transform var(--rp-med), border-color var(--rp-med), box-shadow var(--rp-med);
}
/* Hover lifts in the PRODUCT's colour, not the house one - the card belongs
   to the plugin, and this is the moment that reads clearest. */
.hub-card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--rp-brand) 55%, transparent);
  box-shadow: 0 30px 66px -34px rgba(0,0,0,.95),
              0 0 62px -28px color-mix(in srgb, var(--rp-brand) 70%, transparent);
}
.hub-card__shot { display: block; position: relative; aspect-ratio: 4 / 3; background: #06070c; }
.hub-card__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ONE RULE for a card: everything on it wears the PRODUCT's colour. The
   ribbon, the pills, the price, the hover CTA and this badge all take
   --rp-brand, so a card reads as one plugin rather than as a house template
   with a plugin dropped into it. The page-level deal bar keeps the house
   purple, because it speaks for the whole shop, not for one product.

   Dark ink for the same reason the corner ribbon uses it: every brand colour
   here is light, and white on them falls under 3:1. */
.hub-card__sale {
  position: absolute; top: .8rem; left: .8rem;
  font-family: var(--rp-font-mono); font-size: .7rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: #0a0b11; background: var(--rp-brand, var(--hub-accent));
  border-radius: 999px; padding: .34em .8em;
}

/* The saving follows the same rule. It was taking --rp-accent, which on
   HardStop is Drive blue - the one element on a clip-red card wearing a colour
   nothing else there wears. */
.hub-card .hs-save { --save-ink: var(--rp-brand, var(--hub-accent)); }
/* Corner ribbon, in the PRODUCT's colour - the same rule as the hover lift:
   the card belongs to the plugin. Anchored to the shot's top-right corner and
   rotated through it; the card's own overflow:hidden does the clipping, so the
   ribbon's ends are cut by the rounded corner rather than drawn outside it.

   Width is fixed and the text centred, so "NEW" and "BEST SELLER" cut the
   corner at the same place instead of the ribbon growing with its wording.

   Dark ink, not white: both product brands are light colours (#ff5765,
   #ab8cff) and white on either falls under 3:1. */
.hub-card__badge {
  position: absolute; top: 1.5rem; right: -3.4rem; width: 12rem;
  transform: rotate(45deg); transform-origin: center;
  text-align: center; padding: .42em 0;
  background: var(--rp-brand); color: #0a0b11;
  font-family: var(--rp-font-mono); font-size: .7rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  box-shadow: 0 6px 18px -8px rgba(0,0,0,.9);
  pointer-events: none;
}

.hub-card__body { display: flex; flex-direction: column; flex: 1; padding: 1.35rem 1.45rem 1.5rem; }
/* WHAT THE PLUGIN IS, above what it is called. Outlined rather than filled:
   the corner ribbon is already a solid block of the product's colour, and two
   filled shapes in the same hue on one card compete. Sits in the body, not on
   the shot, where it would crowd the ribbon and the sale pill. */
/* A row, because a card may carry more than one: what it IS, then what it
   does. They wrap rather than shrink so a long category never squeezes the
   others into initials. */
.hub-card__pills {
  display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .6rem;
}
.hub-card__cat {
  /* One ink per pill, so a card with no product theme behind it can still be
     coloured. --rp-brand is declared by a product's theme; Lo-Fi is published
     elsewhere and has none, hence the fallback. */
  --pill-ink: var(--rp-brand, var(--hub-accent));
  align-self: flex-start;
  padding: .34em .72em; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--pill-ink) 42%, transparent);
  background: color-mix(in srgb, var(--pill-ink) 9%, transparent);
  color: color-mix(in srgb, var(--pill-ink) 78%, white);
  font-family: var(--rp-font-mono); font-size: .66rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; line-height: 1.1;
}

/* Amber, picked to sit with the Lo-Fi artwork's lamplight rather than with a
   house colour it has no claim to. */
.hub-card__cat--amber { --pill-ink: #e8a33d; }

.hub-card__name { font-size: 1.22rem; font-weight: 700; color: var(--rp-text); }
.hub-card__tag { margin-top: .45rem; font-size: 1rem; color: var(--rp-text2); line-height: 1.55; flex: 1; }
.hub-card__foot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-top: 1.3rem; padding-top: 1.1rem;
  border-top: 1px solid var(--rp-border);
}
.hub-card__price { font-size: 1.18rem; color: var(--rp-text); }
.hub-card__price s {
  color: var(--rp-text2); font-size: .84em; margin-right: .2em;
  text-decoration-thickness: .08em;
}
.hub-card__price b { color: var(--rp-brand); }
.hub-card__go {
  font-family: var(--rp-font-mono); font-size: .76rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--hub-muted);
  transition: color var(--rp-med);
}
/* Hover stays the PRODUCT's colour - purple for Clarity, clip red for
   HardStop - so the cards read as one system at rest and as themselves on
   the way to a click. */
.hub-card:hover .hub-card__go { color: var(--rp-brand); }

/* --------------------------------------------------------------- magnet --- */
.hub-magnet {
  display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(1.8rem, 3.5vw, 3rem);
  align-items: center;
  padding: clamp(1.8rem, 3.5vw, 2.8rem);
  border: 1px solid color-mix(in srgb, var(--hub-accent) 26%, transparent);
  border-radius: var(--rp-r-lg);
  background: radial-gradient(120% 160% at 12% 0%,
              color-mix(in srgb, var(--hub-accent) 10%, transparent), transparent 60%),
              var(--rp-panel);
}
@media (max-width: 820px) { .hub-magnet { grid-template-columns: minmax(0, 1fr); } }
/* SUBORDINATE to the line above the card. The section's headline is
   .hub-magnet__above, at the same 48.8px as FLAGSHIP PLUGINS and BEHIND THE
   PLUGINS; this is the card's own heading and was matching it exactly, so two
   full-size headlines stacked and neither led. */
.hub-magnet__head {
  margin: .8rem 0 .9rem;
  font-size: clamp(1.45rem, 2.6vw, 2rem);
}

/* Body copy at body size. This paragraph was running at the lede size used for
   one-line section intros, which made it the largest block of running text on
   a page whose other paragraphs sit at 1rem. */
#hubSignup .hs-lede { font-size: 1rem; line-height: 1.7; }

/* The submit button joins the page's button convention - mono, uppercase,
   tracked - so it and the tutorials button read as the same kind of control
   rather than two different design systems. */
.hs-root .hub-form__go {
  font-family: var(--rp-font-mono); font-size: .84rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
}
.hub-form { display: flex; gap: .6rem; }
@media (max-width: 560px) { .hub-form { flex-direction: column; } }
.hub-form__field {
  flex: 1; min-width: 0; height: 52px; padding: 0 1.1rem;
  border-radius: 999px; border: 1px solid var(--rp-border2);
  background: rgba(255,255,255,.03); color: var(--rp-text);
  font-family: inherit; font-size: 1rem;
}
.hub-form__field::placeholder { color: var(--rp-text3); }
.hub-form__field:focus { outline: 2px solid var(--hub-accent); outline-offset: 2px; border-color: transparent; }
.hs-root .hub-form__go { --btn-bg: var(--hub-accent); --btn-fg: var(--hub-ink); height: 52px; padding: 0 1.6rem; white-space: nowrap; }
.hub-form__note {
  margin: .9rem 0 0; font-family: var(--rp-font-mono);
  font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--rp-text3);
}

/* ---------------------------------------------------------------- story --- */
/* ------------------------------------------------------------------ video --- */
/* A click-to-load FACADE, not an iframe. The poster is a local copy of the
   video's own thumbnail, so this page makes no request to youtube.com and sets
   no third-party cookie until a visitor actually asks to watch - the player,
   and everything it carries with it, is their choice rather than the page's.

   It is a <button> because it does something when activated; keyboard focus
   and Enter/Space come free, which a clickable <div> would have to reinvent. */
/* Two up, collapsing to one column when a 16:9 player would get too small
   to read the title on. */
.hub-videos {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: clamp(1.1rem, 2.2vw, 1.7rem);
  margin-top: 1.6rem; text-align: left;
}
.hub-video {
  position: relative; display: block; width: 100%;
  margin: 0; padding: 0; overflow: hidden; cursor: pointer;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--rp-border); border-radius: var(--rp-r-lg);
  background: #06070c;
  transition: border-color var(--rp-med), box-shadow var(--rp-med);
}
/* NO lift on hover - the plugin cards rise because they are the page's primary
   action; a video doing the same competes with them for the same gesture. The
   border and the play button carry the affordance instead. */
.hub-video:hover, .hub-video:focus-visible {
  border-color: color-mix(in srgb, var(--hub-accent) 60%, transparent);
  box-shadow: 0 0 48px -30px color-mix(in srgb, var(--hub-accent) 70%, transparent);
}
.hub-video__poster { width: 100%; height: 100%; object-fit: cover; display: block; }
/* YouTube's own play control: a wide rounded rectangle in their red with a
   white triangle, not a house-coloured circle.

   The ONE place on this page that deliberately ignores the palette. A play
   button is a control people recognise before they read anything, and the
   recognition is the affordance - restyling it in brand purple makes a
   visitor work out what it does. It also sets an honest expectation: this
   opens YouTube, and it looks like it will.

   Literal #ff0000 rather than a token, because it is not ours to theme. */
.hub-video__play {
  position: absolute; inset: 0; margin: auto;
  width: 68px; height: 48px; border-radius: 14px;
  display: grid; place-items: center;
  background: #ff0000; color: #fff;
  box-shadow: 0 10px 28px -10px rgba(0,0,0,.75);
  transition: background var(--rp-med), transform var(--rp-med);
}
/* Their hover behaviour too - the button reacts, the card does not move. */
.hub-video:hover .hub-video__play,
.hub-video:focus-visible .hub-video__play {
  background: #f00; transform: scale(1.06);
}
.hub-video__play svg { width: 26px; height: 26px; margin-left: 2px; }
/* Sits on a scrim rather than straight on the artwork, which cannot be relied
   on to be dark where the text lands. */
.hub-video__title {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 2.6rem 1.15rem .95rem; text-align: left;
  font-size: .95rem; font-weight: 600; line-height: 1.35; color: #fff;
  background: linear-gradient(to top, rgba(0,0,0,.85), transparent);
}
/* Room under the headline: it introduces the thumbnails, and sitting tight
   against them made it read as their caption. */
.hub-video__head { margin: .9rem 0 clamp(1.6rem, 3vw, 2.6rem); }
.hub-video__more { margin: 1rem 0 0; font-size: .92rem; }
.hub-video__more a { text-decoration: none; }
@media (max-width: 600px) {
  .hub-video__play { width: 56px; height: 40px; border-radius: 11px; }
  .hub-video__play svg { width: 22px; height: 22px; }
  .hub-video__title { font-size: .85rem; padding-top: 2rem; }
}

/* A listing that is not published yet. Same card, no lift and no pointer -
   there is nothing to click, and a card that behaves like a link but is not
   one is a small lie told on hover. */
.hub-card--soon { cursor: default; }
.hub-card--soon:hover { transform: none; border-color: var(--rp-border); box-shadow: none; }
.hub-card__soon { color: var(--hub-muted); font-weight: 700; }

/* ---------------------------------------------------------------- about --- */
/* Its own line above the photo, at the size the other section headings use, so
   it reads as a section of the page rather than a label on the right column. */
.hub-about__head {
  margin: 0 0 clamp(1.6rem, 3vw, 2.4rem); text-align: center;
  font-size: clamp(2rem, 4.6vw, 3.05rem); font-weight: 800;
  line-height: 1.04; letter-spacing: .04em; text-transform: uppercase;
  color: var(--rp-text);
}
/* Portrait beside the words, because the photograph IS the credential - he is
   holding the certificate the first line claims. */
.hub-about {
  /* 300px suits the 4:5 portrait: tall enough to read the certificate he is
     holding, which is the credential the line beside it claims. */
  display: grid; grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(1.5rem, 3.5vw, 3rem); align-items: center;
}
.hub-about__shot {
  width: 100%; height: auto; border-radius: var(--rp-r-lg);
  border: 1px solid var(--rp-border);
}
/* Space BETWEEN paragraphs, not above every one, so the first still sits
   tight under the credentials. */
.hub-about__body + .hub-about__body { margin-top: .9rem; }
.hub-about__body {
  margin: 1rem 0 0; max-width: 62ch;
  font-size: 1rem; line-height: 1.7; color: var(--rp-text2);
}
/* The credentials themselves. A row of short claims rather than a sentence -
   they are scanned, not read, and separated by a rule so no two run together. */
.hub-creds {
  list-style: none; margin: .9rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .9rem;
  font-family: var(--rp-font-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--rp-text);
}
.hub-creds li { display: flex; align-items: center; gap: .9rem; }
.hub-creds li + li::before {
  content: ""; width: 4px; height: 4px; border-radius: 50%;
  background: var(--hub-accent);
}
@media (max-width: 720px) {
  .hub-about { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; }
  .hub-about__shot { max-width: 280px; }
  .hub-creds { justify-content: center; }
  .hub-about__body { margin-inline: auto; }
}

/* The line above the signup card. */
.hub-magnet__above {
  margin: 0 0 clamp(1rem, 2vw, 1.6rem); text-align: center; text-wrap: balance;
}

/* --------------------------------------------------- polish additions --- */
/* Some eyebrows carry the shared em-dash marker; these read as labels on their
   own and do not need it. */
.hs-eyebrow--plain::before { display: none; }
.hs-eyebrow--plain { padding-left: 0; }

/* The magnet's thumbnail: 16:9, above the form, so the slot reads as a video. */
.hub-magnet__shot {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
  object-fit: cover; margin-bottom: 1rem;
  border-radius: var(--rp-r-lg); border: 1px solid var(--rp-border);
}

/* Reach, under the qualifications rather than beside them. */
.hub-reach {
  margin: .55rem 0 0;
  font-family: var(--rp-font-mono); font-size: .68rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--hub-muted);
}

/* Closes the credentials block off from the videos under it. */
.hub-rule {
  border: 0; height: 1px; background: var(--rp-border);
  margin: clamp(2.2rem, 4vw, 3.2rem) 0 0;
}

/* THE CHANNEL BUTTON. A real target rather than a text link - it is the only
   way off this page that is not a plugin, so it should look clickable.

   The hover does three things at once, all cheap to composite: the ground
   fills, the whole button lifts, and the arrow slides out from under the
   label. The YouTube glyph stays put as the anchor. */
.hub-ytbtn {
  display: inline-flex; align-items: center; gap: .85rem;
  margin-top: .9rem; padding: 1.05em 2.1em;
  border-radius: 999px; text-decoration: none;
  border: 1px solid color-mix(in srgb, var(--hub-accent) 45%, transparent);
  background: color-mix(in srgb, var(--hub-accent) 10%, transparent);
  color: var(--rp-text);
  font-family: var(--rp-font-mono); font-size: .84rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; line-height: 1;
  /* Slower than the page default: a big button that snaps looks cheap, and
     the whole point of this one is that it feels considered. */
  transition: background 420ms ease, border-color 420ms ease,
              transform 420ms cubic-bezier(.16,.84,.44,1), box-shadow 420ms ease;
}
.hub-ytbtn__ico { width: 23px; height: 23px; color: #ff0000; flex: none; }
.hub-ytbtn__arrow {
  width: 15px; height: 15px; flex: none;
  transform: translateX(-4px); opacity: .55;
  transition: transform var(--rp-med), opacity var(--rp-med);
}
.hub-ytbtn:hover, .hub-ytbtn:focus-visible {
  background: color-mix(in srgb, var(--hub-accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--hub-accent) 80%, transparent);
  transform: translateY(-3px);
  /* Two shadows: a soft coloured bloom around the button and a shadow beneath
     it, so it reads as lifting off the page rather than merely glowing. */
  box-shadow: 0 0 34px -6px color-mix(in srgb, var(--hub-accent) 42%, transparent),
              0 22px 40px -22px rgba(0,0,0,.95);
}
.hub-ytbtn:hover .hub-ytbtn__arrow,
.hub-ytbtn:focus-visible .hub-ytbtn__arrow { transform: translateX(2px); opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .hub-ytbtn, .hub-ytbtn__arrow { transition: none; }
  .hub-ytbtn:hover { transform: none; }
}

/* ------------------------------------------------------------ quote line --- */
/* One endorsement on a single row, under the hero tagline. Wraps to two lines
   on a phone rather than shrinking the type, and the credit stays with the
   name because a quote whose attribution has wrapped away is worth less than
   no quote. */
.hub-quoteline {
  margin: 1.1rem auto 0; max-width: 74ch;
  display: flex; align-items: center; justify-content: center;
  gap: .55rem .75rem; flex-wrap: wrap;
  font-size: clamp(.98rem, 1.5vw, 1.08rem); line-height: 1.5;
  color: var(--rp-text);
}
.hub-quoteline__face {
  width: 34px; height: 34px; border-radius: 50%; object-fit: cover;
  border: 1px solid color-mix(in srgb, var(--hub-accent) 50%, transparent);
  flex: none;
}
.hub-quoteline__quote { font-style: italic; }
.hub-quoteline__by {
  font-style: normal; white-space: nowrap;
  font-family: var(--rp-font-mono); font-size: .68rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--hub-muted);
}

/* --------------------------------------------------------------- footer --- */
.hub-foot { border-top: 1px solid var(--rp-border); margin-top: clamp(3rem, 6vw, 5rem); padding: 2.2rem 0 3rem; }
.hub-foot .hs-wrap { display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
.hub-foot__mark,
.hub-foot__links a {
  font-family: var(--rp-font-mono); font-size: .78rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--rp-text3);
}
.hub-foot__links { display: flex; gap: 1.6rem; flex-wrap: wrap; }
.hub-foot__links a:hover { color: var(--hub-accent); }

/* ------------------------------------------------------ purchase toast --- */
/* Real sales only, from the Worker feed. Fixed and off-flow so it can never
   shift the page, and translated rather than animated on top/left so the
   browser composites it without laying the page out again. */
.hs-fomo {
  position: fixed; left: 1rem; bottom: 1rem; z-index: 60;
  display: flex; align-items: center; gap: .75rem;
  max-width: min(92vw, 340px);
  padding: .7rem .85rem .7rem 1rem;
  border-radius: var(--rp-r-lg);
  border: 1px solid var(--rp-border);
  background: color-mix(in srgb, var(--rp-panel, #0d0e15) 92%, transparent);
  backdrop-filter: blur(8px);
  box-shadow: 0 22px 44px -22px rgba(0,0,0,.95);
  color: var(--rp-text);
  transform: translateY(140%); opacity: 0;
  transition: transform 520ms cubic-bezier(.16,.84,.44,1), opacity 320ms ease;
}
.hs-fomo.is-in { transform: translateY(0); opacity: 1; }
.hs-fomo__text { font-size: .86rem; line-height: 1.45; }
.hs-fomo__text b { color: var(--rp-text); }
.hs-fomo__when {
  display: block; margin-top: .1rem;
  font-family: var(--rp-font-mono); font-size: .62rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--rp-text3);
}
.hs-fomo__close {
  margin-left: auto; padding: .2rem .4rem; border: 0; border-radius: 6px;
  background: none; color: var(--rp-text3); cursor: pointer; font-size: 1rem;
  line-height: 1;
}
.hs-fomo__close:hover { color: var(--rp-text); }
@media (max-width: 600px) { .hs-fomo { display: none; } }
@media (prefers-reduced-motion: reduce) { .hs-fomo { transition: none; } }

/* ------------------------------------------------------- card rating --- */
/* Opposite the price, in the space the CTA text used to hold. Sized to sit
   level with the price rather than shout over it - the number beside the stars
   is the point, since five stars from six people and five stars from six
   hundred are not the same claim. Uses the house accent, not the product's:
   the card is speaking for the shop here, the same way the sale chip does. */
.hub-card__rating {
  display: inline-flex; align-items: center; gap: .12rem;
  color: var(--hub-accent, #7c5cff);
  white-space: nowrap;
}
.hub-card__rating svg { width: 17px; height: 17px; }
.hub-card__rcount {
  margin-left: .3rem;
  font-family: var(--rp-font-mono); font-size: .78rem; font-weight: 700;
  color: var(--hub-muted, #8b90a8);
}
/* Below this the price and the rating start competing for the same line, and
   the price wins - so the word goes and the bare number stays. Measured, not
   guessed: at a 360px card "6 reviews" leaves 29px of slack and "128 reviews"
   leaves 16px, at which point the price begins wrapping. */
@media (max-width: 420px) {
  .hub-card__rating svg { width: 15px; height: 15px; }
  .hub-card__rcount { font-size: .7rem; }
  .hub-card__rword { display: none; }
}

/* Clarity Delay - tier-2 signature tokens. VALUES ONLY: one selector block,
   no extra rules. Declared on .hs-root .hub-card--clarity-delay, matching the tier-2 convention every
   product's theme.css follows - not :root, which would leak into the rest of
   the WordPress page and, worse,
   sit farther from the content than tier 1's own .hs-root .hub-card--clarity-delay default, so the
   nearer ancestor (tier 1) would silently win over this override.

   PALETTE SOURCE: the brand guide supplied by the product's designer
   (2026-08-17) - four values, used here as-is rather than reinterpreted:
     #24F1E0  teal    -> --rp-accent
     #FFA98C  peach   -> --rp-brand   (already this product's brand colour)
     #AB8CFF  purple  -> --rp-accent-2
     #566D7D  slate   -> --rp-text2 / --rp-border2, the supporting greys
   The teal and purple are the same two colours the plugin's own GUI uses for
   its meters and knobs, so the page and the product now agree.

   The soft variants are not from the guide - they are lightened forms of the
   guide's own values, needed because several components take a second, paler
   step of the same hue (gradients, hover states). They are derived from the
   supplied colours, not invented alongside them. */
.hs-root .hub-card--clarity-delay {
  /* Purple is the lead signature colour: --rp-brand is what paints the hero
     headline gradient, the ambient wash behind it, the rim light around the
     plugin shot and the logo drop-shadow, so it carries all of those. Peach
     is not dropped - it moves to --rp-accent-2, where it still colours the
     cards and accents that slot uses. Both are the guide's own values; only
     which role each plays has changed. */
  --rp-brand:          #ab8cff;  /* guide: purple */
  --rp-accent:         #24f1e0;  /* guide: teal */
  --rp-accent-soft:    #86f7ec;  /* lightened teal */
  --rp-accent-2:       #ffa98c;  /* guide: peach */
  --rp-accent-2-soft:  #ffc9b5;  /* lightened peach */
  --rp-good:           #24f1e0;  /* the guide carries no green; teal reads as
                                    affirmative here and matches the GUI */
  --rp-warn:           #ffa98c;
  --rp-on-accent:      #0a0b11;  /* all four guide colours are light, so dark
                                    ink is what stays legible on them */

  /* Supporting greys, pulled toward the guide's slate. */
  --rp-text2:          #8ea4b2;
  --rp-border2:        #566d7d;  /* guide: slate */

  /* Tier-1 override. --rp-text3 is the FAINT text step - it colours the
     testimonial credit line, the review channel name, price notes, footer
     links and legal, the sysreq labels and the mode descriptions. Set to the
     guide's teal rather than a grey, so those lines read as a deliberate
     accent instead of receding. */
  --rp-text3:          #24f1e0;

  /* The guide specifies Catamaran. Registered in build.py's FONTS <link>, so
     it is actually fetched rather than silently falling through. Tier 1's
     stack stays behind it as the fallback. The guide's tracking of 100
     (0.1em) is deliberately NOT applied globally: at display sizes it pulls
     the headline apart badly. Tracking is exposed in the studio panel so it
     can be judged per level rather than set blind. */
  --rp-font-display: "Catamaran", "Space Grotesk", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* HardStop - tier-2 signature tokens. VALUES ONLY: one selector block, no
   extra rules. Declared on .hs-root .hub-card--hardstop, matching tier 1 in shared/css/tokens.css
   -- not :root, which would leak into the rest of the WordPress page and,
   worse, sit farther from the content than tier 1's own .hs-root .hub-card--hardstop default,
   so the nearer ancestor (tier 1) would silently win over this override.
   Waveform-widget colours are tier 3 and live in section.css. */
.hs-root .hub-card--hardstop {
  --rp-brand:          #ff5765;  /* clip red - also the logo bar */
  --rp-accent:         #3aa6ff;  /* Drive blue */
  --rp-accent-soft:    #7cc6ff;
  --rp-accent-2:       #ff5a8c;  /* Ceiling pink */
  --rp-accent-2-soft:  #ff92b5;
  --rp-good:           #34d399;  /* TP safety green */
  --rp-warn:           #ff9e3d;
  --rp-on-accent:      #edeae4;
}
