/* Built by store-checkout/build.mjs. Edit the sources, not this file. */

/* =========================================================================
   ABL design tokens — pilot for the site rebuild
   =========================================================================

   This file is deliberately NOT built on the current site's conventions.

   The live Webflow site sets `body { font-size: 1vw }` with no breakpoint
   override, so every `em` on the site is a fraction of viewport width. That
   makes third-party UI (Stripe, date pickers, anything with its own CSS)
   impossible to size consistently, and it is why the old checkout snippet
   reached for `rem` and immediately desynchronised from the page around it.

   The live site also sets body `color` and `background-color` to the same
   value (#140B00), so every new element starts with invisible text until a
   class rescues it.

   Both are fixed here. Checkout is the pilot: it is isolated, it has no nav,
   and it is the highest-value surface on the site, so it is the cheapest
   place to prove the new system before the rebuild extends it.

   Everything below is scoped to [data-abl] so it can live on one page
   alongside the existing stylesheet without fighting it.
   ========================================================================= */

[data-abl] {
  /* --- Brand ------------------------------------------------------------
     The four values carried over from the current site. These are correct
     and the brand reads well; only the naming and the alpha ramp are new.
     NOTE: the old checkout snippet used #F5A83C for its CTA. That colour
     appears nowhere else in the 702-class site stylesheet. The real accent
     is #FFB442. The snippet was off-brand.
     ------------------------------------------------------------------- */
  --abl-brand-ink:        #FFF1E0;  /* was "Biege" (sic) */
  --abl-brand-surface:    #140B00;  /* was "Brown TxT" */
  --abl-brand-deep:       #0A0600;  /* was "Dark Brown" */
  --abl-brand-accent:     #FFB442;  /* was "Primary" */

  /* --- Semantic surfaces ------------------------------------------------
     Webflow variables have no alpha modifier, which is why the current site
     hardcodes hsla(32.9,100%,93.92%,.25/.3/.35/.65/.85) in a dozen places.
     Declaring the ramp once here is the fix.
     ------------------------------------------------------------------- */
  --abl-surface:          var(--abl-brand-surface);
  --abl-surface-deep:     var(--abl-brand-deep);
  --abl-surface-raised:   #1C1108;
  --abl-surface-inset:    #0E0803;

  --abl-ink:              var(--abl-brand-ink);
  /* Raised 2026-07-26 after reviewing the built page on a real screen. The
     first pass was tuned by eye against the token file, not against #140B00
     at real sizes, and 0.46 on 13px hint text was genuinely hard to read.
     Contrast against the #140B00 ground at these values:
       ink        #FFF1E0   ~16.5:1   (AAA)
       ink-muted  0.80      ~11.0:1   (AAA)
       ink-subtle 0.62      ~6.8:1    (AA, and AAA for the 19px+ uses)
       ink-faint  0.42      ~3.6:1    placeholders and disabled only, never
                                      load-bearing text */
  --abl-ink-muted:        rgb(255 241 224 / 0.80);
  --abl-ink-subtle:       rgb(255 241 224 / 0.62);
  --abl-ink-faint:        rgb(255 241 224 / 0.42);

  --abl-line:             rgb(255 241 224 / 0.18);
  --abl-line-strong:      rgb(255 241 224 / 0.34);
  --abl-line-accent:      rgb(255 180 66 / 0.42);

  --abl-accent:           var(--abl-brand-accent);
  --abl-accent-hover:     #FFC163;
  --abl-accent-ink:       #140B00;
  --abl-accent-wash:      rgb(255 180 66 / 0.10);

  /* Semantic states. The current site has no error or success colour at all,
     which is why the old snippet invented #FFB4A2 as a one-off. */
  --abl-danger:           #FF9B8A;
  --abl-danger-wash:      rgb(255 155 138 / 0.12);
  --abl-success:          #8FCB9B;
  --abl-success-wash:     rgb(143 203 155 / 0.12);

  /* --- Type -------------------------------------------------------------
     Same typeface as the site (PP Neue Montreal, self-hosted, 400 + 500
     only). Weights 300 and 700 appear in the site CSS with no matching font
     file, so they render as faux weights; they are not offered here.

     Fluid clamp scale off a 16px root, NOT off 1vw. Values are tuned to hold
     between a 360px phone and a 1600px desktop.
     ------------------------------------------------------------------- */
  --abl-font: "PP Neue Montreal", -apple-system, BlinkMacSystemFont,
              "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --abl-weight-regular: 400;
  --abl-weight-medium:  500;

  /* 16px floor on every input-adjacent size: iOS Safari zooms the viewport
     on focus when the computed font-size is below 16px. */
  --abl-text-2xs:  0.75rem;                                  /* 12 */
  --abl-text-xs:   0.8125rem;                                /* 13 */
  --abl-text-sm:   0.875rem;                                 /* 14 */
  --abl-text-base: 1rem;                                     /* 16 — inputs */
  --abl-text-md:   clamp(1.0625rem, 1.02rem + 0.20vw, 1.1875rem);
  --abl-text-lg:   clamp(1.25rem,   1.18rem + 0.32vw, 1.5rem);
  --abl-text-xl:   clamp(1.5rem,    1.36rem + 0.62vw, 2rem);
  --abl-text-2xl:  clamp(1.875rem,  1.64rem + 1.04vw, 2.75rem);

  --abl-leading-tight: 1.15;
  --abl-leading-snug:  1.35;
  --abl-leading-body:  1.55;

  --abl-tracking-tight: -0.015em;
  --abl-tracking-wide:   0.06em;

  /* --- Space ------------------------------------------------------------
     4px base. The current site has no scale: .wrapper_general is
     `padding: 152px 32px`, .container is `padding: 8px 0`, and the plan
     classes are all `em` against a 1vw root.
     ------------------------------------------------------------------- */
  --abl-space-1:  0.25rem;
  --abl-space-2:  0.5rem;
  --abl-space-3:  0.75rem;
  --abl-space-4:  1rem;
  --abl-space-5:  1.25rem;
  --abl-space-6:  1.5rem;
  --abl-space-8:  2rem;
  --abl-space-10: 2.5rem;
  --abl-space-12: 3rem;
  --abl-space-16: 4rem;

  /* --- Radii ------------------------------------------------------------
     The site has eight radii by accretion (100%, 6px, 4px, 100em, 3px, 50%,
     5px, 1.25em). Three plus a pill is enough.
     ------------------------------------------------------------------- */
  --abl-radius-sm:   6px;
  --abl-radius-md:   10px;
  --abl-radius-lg:   16px;
  --abl-radius-pill: 999px;

  /* --- Elevation & motion ---------------------------------------------- */
  --abl-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.30);
  --abl-shadow-md: 0 6px 24px rgb(0 0 0 / 0.35);
  --abl-focus-ring: 0 0 0 3px rgb(255 180 66 / 0.34);

  --abl-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --abl-speed: 160ms;

  /* --- Controls ---------------------------------------------------------
     48px minimum. WCAG 2.2 SC 2.5.8 sets 24px as the AA floor, which is a
     legal minimum, not a design target. Baymard's mobile guidance is 48px
     including field labels.
     ------------------------------------------------------------------- */
  --abl-control-height: 48px;
  --abl-tap-min: 48px;

  --abl-measure: 34rem;      /* readable line length for body copy */
  --abl-page-max: 68rem;     /* two-column checkout shell */
}

/* Respect the OS. Nothing here animates anything load-bearing, but the
   scroll behaviour and transitions should still yield. */
@media (prefers-reduced-motion: reduce) {
  [data-abl] * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* =========================================================================
   ABL checkout components
   =========================================================================

   Built on abl-tokens.css. Every class is prefixed `abl-` and every value
   comes from a token, so this set ports to Webflow classes, to a React
   component library, or to a future rebuild without a rewrite.

   Component inventory (the reusable set):
     abl-shell / abl-shell__main / abl-shell__aside   page frame
     abl-topbar                                       minimal checkout header
     abl-next                                         "after you pay" timeline
     abl-summary                                      order summary card
     abl-mobile-bar                                   sticky plan context (mobile)
     abl-panel  (+ --secure)                          bordered section
     abl-field / abl-label / abl-input / abl-select   form primitives
     abl-hint / abl-error                             field support text
     abl-check  (+ --required / --optional)           consent rows
     abl-disclose                                     the pre-payment terms block
     abl-btn    (+ --primary / --ghost / --block)     buttons
     abl-guarantee                                    refund promise line
     abl-divider                                      "or pay with card"
     abl-stack / abl-cluster                          layout primitives
     abl-skeleton                                     reserved-space loaders
   ========================================================================= */

/* --- Reset, scoped ------------------------------------------------------ */

[data-abl],
[data-abl] *,
[data-abl] *::before,
[data-abl] *::after { box-sizing: border-box; }

/* 🚨 SCOPED TO THE ROOT, and it must stay that way.
 *
 * This was `[data-abl]`, which looks like a page-shell selector and is not.
 * `data-abl` is our HOOK attribute: forty-odd elements carry it, including
 * `<span data-abl="summary-plan">`, `<span data-abl="summary-amount">` and
 * `<span data-abl="pay-label">`. So `background-color: var(--abl-surface)` was
 * painting the PAGE GROUND as an opaque rectangle on every hooked element,
 * directly on top of whatever panel it sat on.
 *
 * Visible result: a dark block behind the plan name and the price in the summary
 * rail, and — worst of all — a dark block covering the label on the gold pay
 * button, which is what made the primary call to action look broken. It was
 * misdiagnosed for most of 2026-07-30 as a Safari rendering artefact, because it
 * happened to be reported from Safari and nobody had inspected the computed style.
 * Found by asking the browser what was actually painting there.
 *
 * `data-abl-checkout` is on the body and nowhere else, so it means the page shell
 * and only the page shell. Every property in this block is a root/inherited
 * concern — margin, the font stack, colour, the ground — so none of it was ever
 * meant to apply per element.
 *
 * If you need to style one hooked element, give it a class. Do not widen this. */
[data-abl-checkout] {
  margin: 0;
  font-family: var(--abl-font);
  font-weight: var(--abl-weight-regular);
  font-size: var(--abl-text-base);
  line-height: var(--abl-leading-body);
  /* The site's body rule sets colour and background to the same value, so both
     are stated explicitly here or the page inherits invisible text. */
  color: var(--abl-ink);
  background-color: var(--abl-surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

[data-abl] h1, [data-abl] h2, [data-abl] h3, [data-abl] p { margin: 0; }

/* 🚨 NEUTRALISE THE SITE'S BARE-TAG FONT SIZES. Do not remove.
 *
 * The Webflow site stylesheet sizes bare tags with `em` multipliers inside mobile
 * media queries:
 *     @media (max-width: 991px) { p, ul     { font-size: 1.5em } }
 *     @media (max-width: 767px) { p, ul     { font-size: 2.15em } }
 *     @media (max-width: 479px) { p, ul, ol { font-size: 4em } }
 *     @media (max-width: 479px) { h2 { font-size: 6.5em } h3 { font-size: 4.5em } }
 *
 * Those are correct on the marketing site, where `body { font-size: 1vw }` makes
 * 4em roughly 15px at phone width. This page deliberately sets a FIXED 16px base
 * so iOS does not zoom on focus — which turns the same 4em into 64px.
 *
 * Result before this rule: at 390px every paragraph without a size-setting class
 * rendered at 64px, one word per line, and the plan description alone filled the
 * first screen of a page asking for $199.99. Headings escaped only because their
 * classes happen to set a size. Found 2026-07-30, the first time anyone rendered
 * this page at 390px — which is where 80 to 92% of this category's traffic is.
 *
 * `:where()` contributes ZERO specificity, so this whole selector is (0,1,0):
 * strong enough to beat the site's bare tags at (0,0,1), and a deliberate TIE with
 * our own single-class type rules, every one of which is defined later in this file
 * and therefore wins. That is why this block must stay ABOVE them. */
[data-abl-checkout] :where(p, ul, ol, li, h1, h2, h3, h4, h5, h6, dd, dt, figure, blockquote) {
  font-size: inherit;
  line-height: inherit;
}

/* The site self-hosts PP Neue Montreal in Regular 400 and Medium 500 ONLY.
   A <strong> left at the browser default resolves to `bolder` (~700), which
   has no real face, so the browser synthesises one: smeared, wrongly-spaced
   fake-bold. 500 is the heaviest weight that actually exists here.

   Set once, scoped, rather than per component: every emphasis span added
   later inherits the correct ceiling instead of re-finding this trap. Any
   component wanting a different colour still overrides it locally.
   Lifting this to 600/700 is a site-level job (ship the font file first). */
[data-abl] strong, [data-abl] b { font-weight: var(--abl-weight-medium); }

/* SET COLOUR EXPLICITLY. Do not rely on inheritance from [data-abl].
   The site has tag styles on h1/h2/h3 that set `color` directly, and a
   directly-set colour on an element always beats an inherited one no matter
   how specific the ancestor selector is. The site's h2 colour is
   --brown-txt (#140B00), the same value as the page background, so every
   section heading here rendered as invisible-on-invisible until this rule
   existed. This is the same defect the Webflow audit flagged at body level;
   it just reappears per tag.
   Specificity 0,1,1 beats the site's bare tag selectors at 0,0,1. */
[data-abl] h1, [data-abl] h2, [data-abl] h3,
[data-abl] h4, [data-abl] p, [data-abl] li,
[data-abl] dt, [data-abl] dd, [data-abl] span,
[data-abl] label, [data-abl] strong, [data-abl] b {
  color: inherit;
}
[data-abl] a { color: inherit; }

/* The `hidden` attribute is implemented by the UA stylesheet as
   `display: none`, which ANY author display declaration overrides. Several
   things here are hidden while also carrying .abl-stack (display:flex) or
   .abl-shell__grid (display:grid), so hide() silently did nothing: the
   payment section, the express-wallet wrapper and the checkout body all
   stayed on screen when they should not have. Scoped !important is the
   correct fix; the alternative is remembering this trap at every call site. */
[data-abl] [hidden] { display: none !important; }

/* --- Layout primitives -------------------------------------------------- */

.abl-stack { display: flex; flex-direction: column; }
.abl-stack > * + * { margin-top: var(--abl-gap, var(--abl-space-4)); }
.abl-stack--tight  { --abl-gap: var(--abl-space-2); }
.abl-stack--loose  { --abl-gap: var(--abl-space-6); }
.abl-stack--section{ --abl-gap: var(--abl-space-8); }

.abl-cluster {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--abl-space-3);
}
.abl-cluster--between { justify-content: space-between; }

/* --- Shell -------------------------------------------------------------- */

.abl-shell {
  max-width: var(--abl-page-max);
  margin-inline: auto;
  padding: var(--abl-space-6) var(--abl-space-5) var(--abl-space-16);
}

/* .abl-shell is deliberately NOT an .abl-stack: its last child is the two-column
   grid, and making the shell a flex column changes how that grid sizes. So the
   blocks ABOVE the grid space themselves, and they have to use the SAME token or
   the page reads as uneven — which it did. The title carried this margin as an
   inline style and the plan picker carried nothing at all, so "About you" sat
   flush against the plan cards with no gap. Reported 2026-07-30.

   Add any future direct child of .abl-shell to this selector rather than giving
   it its own margin. */
.abl-shell > .abl-shell__lead,
.abl-shell > [data-abl="plan-picker"] { margin-bottom: var(--abl-space-10); }

.abl-shell__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--abl-space-8);
  align-items: start;
}

/* Two-column above 900px. The summary becomes a sticky rail so plan context
   never leaves the screen: 12% of shoppers abandon because they cannot see
   total cost up front (Baymard).

   ⚠️ THE `order` PAIR IS LOAD-BEARING AND IS NOT COSMETIC TIDYING.
   .abl-shell__aside comes FIRST in the DOM (see checkout.html) because it holds
   "What is included", a material term that must be disclosed before billing
   information is collected. Source order is what governs the single-column
   mobile layout, where there is no way to reorder without this being true.
   Desktop then puts the rail back on the RIGHT with `order`, so the two-column
   appearance is unchanged from before 2026-08-03 while the reading order on
   phones is fixed.
   Delete these two declarations and desktop silently flips to summary-left,
   form-right. Reverse them in the DOM instead and mobile silently puts the
   inclusion list below the pay button again, which is the defect this fixed. */
@media (min-width: 900px) {
  .abl-shell__grid { grid-template-columns: minmax(0, 1.45fr) minmax(19rem, 0.9fr); }
  .abl-shell__main  { order: 1; }
  .abl-shell__aside { order: 2; position: sticky; top: var(--abl-space-6); }
}

/* --- Minimal top bar ----------------------------------------------------
   Nav is stripped. Baymard's "enclosed checkout": leave only a logo escape
   hatch so users cannot pogo-stick back into the marketing site mid-payment.
   ---------------------------------------------------------------------- */

.abl-topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--abl-space-4);
  max-width: var(--abl-page-max);
  margin-inline: auto;
  padding: var(--abl-space-5) var(--abl-space-5);
  border-bottom: 1px solid var(--abl-line);
}
.abl-topbar__mark {
  height: 1.15rem; width: auto; display: block;
  /* The site asset is beige. brightness(0) flattens it to black, invert(1)
     lifts it to pure white, so this yields white monochrome from ANY source
     colour and survives an asset swap without a style change. */
  filter: brightness(0) invert(1);
}
.abl-topbar__logo {
  display: inline-flex; align-items: center; gap: var(--abl-space-2);
  font-size: var(--abl-text-md);
  font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-tight);
  text-decoration: none;
  min-height: var(--abl-tap-min);
}
.abl-topbar__help {
  display: inline-flex; align-items: center; gap: var(--abl-space-2);
  min-height: var(--abl-tap-min);
  font-size: var(--abl-text-sm);
  color: var(--abl-ink-muted);
  text-decoration: none;
}
.abl-topbar__help strong { color: var(--abl-ink); font-weight: var(--abl-weight-medium); }

/* --- What happens next --------------------------------------------------
   Deliberately NOT a progress indicator. There is no stepper on this page.

   An earlier draft had one, and it was wrong: a progress indicator is a
   navigational device for a flow the user is currently inside, and two of
   the four steps here happen off-page over several days with no UI to
   return to. It would have been a status display that can never show
   status. Baymard's rule that an indicator must be a 1:1 map of the process
   cuts against it, not for it.

   So this is a timeline in a different visual language on purpose: relative
   time labels down the left, no dots, no connecting line, no current-state
   styling. Nothing here implies the page will update.
   ---------------------------------------------------------------------- */

.abl-next { list-style: none; padding: 0; margin: 0; }
.abl-next li {
  display: grid;
  grid-template-columns: 1rem 5.5rem 1fr;
  gap: var(--abl-space-3);
  font-size: var(--abl-text-sm);
  line-height: var(--abl-leading-body);
  color: var(--abl-ink-muted);
}
/* Gold, matching the accent used for the step labels beside them. */
.abl-next__ic { color: var(--abl-accent); margin-top: 0.15em; }
.abl-next li + li { margin-top: var(--abl-space-4); }
.abl-next__when {
  font-size: var(--abl-text-2xs);
  font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-wide);
  text-transform: uppercase;
  color: var(--abl-accent);
  padding-top: 0.15em;
}
@media (max-width: 420px) {
  .abl-next li { grid-template-columns: 1fr; gap: var(--abl-space-1); }
}

/* --- Headings ----------------------------------------------------------- */

.abl-title {
  color: var(--abl-ink);
  font-size: var(--abl-text-2xl);
  font-weight: var(--abl-weight-medium);
  line-height: var(--abl-leading-tight);
  letter-spacing: var(--abl-tracking-tight);
}
.abl-subtitle {
  font-size: var(--abl-text-md);
  color: var(--abl-ink-muted);
  max-width: var(--abl-measure);
}
.abl-legend {
  font-size: var(--abl-text-2xs);
  font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-wide);
  text-transform: uppercase;
  color: var(--abl-ink-subtle);
}
.abl-h3 {
  color: var(--abl-ink);
  font-size: var(--abl-text-lg);
  font-weight: var(--abl-weight-medium);
  line-height: var(--abl-leading-snug);
  letter-spacing: var(--abl-tracking-tight);
}

/* --- Panel -------------------------------------------------------------- */

.abl-panel {
  background: var(--abl-surface-raised);
  border: 1px solid var(--abl-line);
  border-radius: var(--abl-radius-lg);
  padding: var(--abl-space-6);
}
@media (max-width: 560px) {
  .abl-panel { padding: var(--abl-space-5) var(--abl-space-4); }
}

/* The payment area gets treatment used NOWHERE else on the page.
   Baymard: users judge security by whether the card fields look secure, and
   the effect only works if the encapsulation is unique to that region. 19%
   of shoppers abandon over card trust. Hence the accent border, the inset
   well, and exactly two icons in the header. */
.abl-panel--secure {
  background: var(--abl-surface-inset);
  border-color: var(--abl-line-accent);
  box-shadow: var(--abl-shadow-md), inset 0 1px 0 rgb(255 241 224 / 0.04);
}
.abl-panel__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--abl-space-3);
  margin-bottom: var(--abl-space-5);
}
.abl-panel__marks {
  display: inline-flex; align-items: center; gap: var(--abl-space-2);
  color: var(--abl-ink-subtle);
}
.abl-panel__marks svg { display: block; }

/* --- Order summary ------------------------------------------------------ */

.abl-summary {
  background: var(--abl-surface-raised);
  border: 1px solid var(--abl-line);
  border-radius: var(--abl-radius-lg);
  padding: var(--abl-space-6);
}
.abl-summary__plan {
  color: var(--abl-ink);
  font-size: var(--abl-text-lg);
  font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-tight);
}
.abl-summary__price {
  display: flex; align-items: baseline; gap: var(--abl-space-2);
}
.abl-summary__amount {
  color: var(--abl-ink);
  font-size: var(--abl-text-2xl);
  font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-tight);
  line-height: 1;
}
.abl-summary__cadence { font-size: var(--abl-text-sm); color: var(--abl-ink-muted); }
.abl-summary__rule { height: 1px; background: var(--abl-line); border: 0; margin: 0; }
.abl-summary__row {
  display: flex; justify-content: space-between; gap: var(--abl-space-4);
  font-size: var(--abl-text-sm);
}
.abl-summary__row dt { color: var(--abl-ink-muted); }
.abl-summary__row dd { margin: 0; text-align: right; }
.abl-summary__change {
  font-size: var(--abl-text-sm);
  color: var(--abl-ink-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  display: inline-flex; align-items: center;
  min-height: var(--abl-tap-min);
}
.abl-summary__change:hover { color: var(--abl-ink); }

/* Mobile: the aside stacks first so plan context is seen before any field,
   then a compact sticky bar keeps it on screen through the whole form. */
.abl-mobile-bar {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--abl-space-3);
  padding: var(--abl-space-3) var(--abl-space-5);
  padding-bottom: calc(var(--abl-space-3) + env(safe-area-inset-bottom, 0px));
  /* Opaque, and NO backdrop-filter. It was blur(10px) behind a 94%-opaque fill,
     so it bought essentially nothing visually, while a position:fixed element with
     backdrop-filter is a well-known source of Safari compositing bugs — elements
     painting black being one of them. JB reports black blocks over text in Safari
     that do not reproduce in Chrome, so removing a known hazard that costs us
     nothing is the right trade even though it is not a confirmed cause. */
  background: rgb(10 6 0 / 0.985);
  border-top: 1px solid var(--abl-line);
  font-size: var(--abl-text-sm);
}
.abl-mobile-bar strong { font-weight: var(--abl-weight-medium); }
.abl-mobile-bar[hidden] { display: none; }
@media (min-width: 900px) { .abl-mobile-bar { display: none; } }

/* --- Fields -------------------------------------------------------------
   Labels above, never inline placeholders, one column, never split a name
   or phone across inputs. All Baymard mobile guidelines; the touch keyboard
   eats 50 to 80% of the screen, so a field seen in isolation must still
   make sense on its own.
   ---------------------------------------------------------------------- */

.abl-field { display: flex; flex-direction: column; gap: var(--abl-space-2); }

.abl-label {
  color: var(--abl-ink);
  font-size: var(--abl-text-sm);
  font-weight: var(--abl-weight-medium);
  color: var(--abl-ink);
}
.abl-label__opt { font-weight: var(--abl-weight-regular); color: var(--abl-ink-subtle); }

.abl-input,
.abl-select {
  width: 100%;
  min-height: var(--abl-control-height);
  padding: 0 var(--abl-space-4);
  font: inherit;
  font-size: var(--abl-text-base);      /* 16px floor: stops iOS focus zoom */
  color: var(--abl-ink);
  background: var(--abl-surface-inset);
  border: 1px solid var(--abl-line-strong);
  border-radius: var(--abl-radius-md);
  transition: border-color var(--abl-speed) var(--abl-ease),
              box-shadow var(--abl-speed) var(--abl-ease);
  appearance: none;
}
.abl-input::placeholder { color: var(--abl-ink-faint); }
.abl-input:focus-visible,
.abl-select:focus-visible {
  outline: none;
  border-color: var(--abl-accent);
  box-shadow: var(--abl-focus-ring);
}
.abl-input[aria-invalid="true"],
.abl-select[aria-invalid="true"] {
  border-color: var(--abl-danger);
  background: var(--abl-danger-wash);
}

.abl-select {
  padding-right: var(--abl-space-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23FFF1E0' stroke-opacity='0.55' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--abl-space-4) center;
}
.abl-select option { background: var(--abl-surface-raised); color: var(--abl-ink); }

.abl-hint {
  font-size: var(--abl-text-xs);
  color: var(--abl-ink-subtle);
  line-height: var(--abl-leading-snug);
}
.abl-error {
  display: flex; align-items: flex-start; gap: var(--abl-space-2);
  font-size: var(--abl-text-xs);
  color: var(--abl-danger);
  line-height: var(--abl-leading-snug);
}
.abl-error[hidden] { display: none; }

/* --- Consent rows -------------------------------------------------------
   The required and optional checkboxes must be unmistakably different
   objects. Several state privacy laws treat health data as sensitive data
   requiring opt-in consent that is freely given and NOT a condition of
   service, so bundling analytics consent into the required terms would
   break in most expansion states even though it holds in Arizona today.
   ---------------------------------------------------------------------- */

.abl-check {
  display: flex; gap: var(--abl-space-3); align-items: flex-start;
  padding: var(--abl-space-4);
  border: 1px solid var(--abl-line);
  border-radius: var(--abl-radius-md);
  cursor: pointer;
  transition: border-color var(--abl-speed) var(--abl-ease),
              background-color var(--abl-speed) var(--abl-ease);
}
.abl-check:hover { border-color: var(--abl-line-strong); }
.abl-check input {
  flex: 0 0 auto;
  width: 1.25rem; height: 1.25rem;
  margin: 0.14rem 0 0;
  accent-color: var(--abl-accent);
  cursor: pointer;
}
.abl-check input:focus-visible { outline: 2px solid var(--abl-accent); outline-offset: 3px; }
.abl-check__text { font-size: var(--abl-text-sm); line-height: var(--abl-leading-body); }
.abl-check a { text-decoration: underline; text-underline-offset: 3px; }

/* Required: sits inside the disclosure block, carries the accent. */
.abl-check--required {
  background: var(--abl-accent-wash);
  border-color: var(--abl-line-accent);
}
.abl-check--required[data-invalid="true"] {
  border-color: var(--abl-danger);
  background: var(--abl-danger-wash);
}

/* Optional: deliberately quiet, and deliberately far from the pay button. */
.abl-check--optional { background: transparent; }
.abl-check--optional .abl-check__text { color: var(--abl-ink-muted); }
.abl-check--optional .abl-check__text strong {
  display: block;
  color: var(--abl-ink);
  font-weight: var(--abl-weight-medium);
  margin-bottom: var(--abl-space-1);
}

/* --- Disclosure block ---------------------------------------------------
   Everything ROSCA and the state auto-renewal laws require, in one visual
   container, before any billing information is collected. California 17602
   and NY GBL 527-a both require the renewal terms be "in visual proximity"
   to the consent request, which is why the checkbox lives inside this box
   rather than next to the pay button.
   ---------------------------------------------------------------------- */

.abl-disclose {
  background: var(--abl-surface-inset);
  border: 1px solid var(--abl-line-strong);
  border-radius: var(--abl-radius-lg);
  padding: var(--abl-space-6);
}
@media (max-width: 560px) {
  .abl-disclose { padding: var(--abl-space-5) var(--abl-space-4); }
}
.abl-disclose__list { list-style: none; padding: 0; margin: 0; }
.abl-disclose__list li {
  display: grid;
  grid-template-columns: 1.15rem 1fr;
  gap: var(--abl-space-3);
  font-size: var(--abl-text-sm);
  line-height: var(--abl-leading-body);
  color: var(--abl-ink-muted);
}
/* Separated rows rather than a single dense stack. This is the list treatment
   the rest of the site uses (see the What's Included lists), and on a block
   this text-heavy the rule does more for legibility than extra tracking would:
   the leading here is already 1.55, looser than the site's own 1.4 body. */
.abl-disclose__list li + li {
  margin-top: var(--abl-space-4);
  padding-top: var(--abl-space-4);
  border-top: 1px solid var(--abl-line);
}
.abl-disclose__list strong { color: var(--abl-ink); font-weight: var(--abl-weight-medium); }
.abl-disclose__list svg { margin-top: 0.32em; flex: 0 0 auto; }
/* One colour for every icon in this list, set 2026-08-03. The refund row used to
   be --abl-success green while the other six were subtle ink, which read as an
   accident rather than a signal.
   Why subtle ink and not the accent gold: --abl-accent is spent on the pay button,
   and seven gold icons directly above it would compete with the one element that
   should be loudest. These icons are structural markers, not calls to action; the
   bold lead sentences carry the hierarchy here.
   The class names stay distinct even though the colour no longer differs, because
   they still describe what each row MEANS in the markup. */
.abl-disclose__list .abl-ic-info,
.abl-disclose__list .abl-ic-good { color: var(--abl-ink-subtle); }

/* --- Buttons ------------------------------------------------------------ */

.abl-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--abl-space-2);
  min-height: var(--abl-tap-min);
  padding: var(--abl-space-3) var(--abl-space-6);
  font: inherit;
  font-size: var(--abl-text-base);
  font-weight: var(--abl-weight-medium);
  border: 1px solid transparent;
  border-radius: var(--abl-radius-pill);
  cursor: pointer;
  text-decoration: none;
  transition: background-color var(--abl-speed) var(--abl-ease),
              border-color var(--abl-speed) var(--abl-ease),
              opacity var(--abl-speed) var(--abl-ease);
}
.abl-btn:focus-visible { outline: none; box-shadow: var(--abl-focus-ring); }

.abl-btn--primary {
  background: var(--abl-accent);
  color: var(--abl-accent-ink);
  /* Own compositing context, so no ancestor filter, blend mode or transform can
     reach in and repaint this button. Cheap insurance on the one control that has
     to render correctly or no money moves. */
  isolation: isolate;
}

/* The pay label states every colour outright instead of inheriting it.
   Belt and braces now that the real cause is fixed at the root: the over-broad
   `[data-abl]` rule above was painting the page ground over this label, which is
   what made the primary call to action look like a black bar. Keeping these
   declarations means the one control that has to be legible or no money moves
   cannot be broken again by a stray background rule further up the cascade.
     - colour set explicitly, not inherited through two wrappers
     - -webkit-text-fill-color set too, which Safari honours over `color` when the
       two disagree
     - background forced transparent, so nothing can paint a box behind the glyphs
     - weight pinned to 500, the heaviest PP Neue Montreal actually ships; 600+
       renders synthetic bold */
.abl-btn__label {
  color: var(--abl-accent-ink);
  -webkit-text-fill-color: var(--abl-accent-ink);
  background: transparent;
  font-weight: var(--abl-weight-medium);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.abl-btn--primary:hover:not(:disabled) { background: var(--abl-accent-hover); }

.abl-btn--ghost {
  background: transparent;
  color: var(--abl-ink);
  border-color: var(--abl-line-strong);
}
.abl-btn--ghost:hover { border-color: var(--abl-ink-subtle); }

.abl-btn--block { width: 100%; }
.abl-btn--lg { min-height: 56px; font-size: var(--abl-text-md); }

/* Note: the pay button is NOT disabled while the form is incomplete. A
   disabled button tells the user nothing about what is wrong, is commonly
   skipped by screen readers, and fails contrast. It is disabled only while
   a confirm is genuinely in flight. Incomplete state is handled by
   submitting, then routing focus to the first problem. */
.abl-btn:disabled { opacity: 0.55; cursor: default; }

/* Submitting. Deliberately NOT the disabled treatment: at the moment a card is
   being charged, a greyed-out button reads as "this failed", and `cursor:
   progress` hands feedback to the OS, which on Safari renders a spinning system
   wheel over a dimmed button and looks like the page hung. Reported 2026-07-30.

   So the button keeps its full accent colour and its own spinner, and only
   pointer-events go away. The label swaps to "Starting your membership…" in
   setBusy(). No opacity change at all: this is the one control on the page where
   looking inert is worse than looking unavailable. */
.abl-btn[data-busy="true"] {
  cursor: default;
  pointer-events: none;
  opacity: 1;
}
/* Slight settle so the state change is felt rather than just seen. */
.abl-btn[data-busy="true"] .abl-spinner { opacity: 0.85; }

.abl-spinner {
  width: 1rem; height: 1rem; flex: 0 0 auto;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: abl-spin 620ms linear infinite;
}
@keyframes abl-spin { to { transform: rotate(360deg); } }

/* --- Guarantee ----------------------------------------------------------
   Rendered as a sentence, not a badge. The one placement A/B test that
   exists found a guarantee below the CTA lifted desktop conversion but
   UNDERperformed on mobile, where users read badge-shaped things as
   decoration and skip them.
   ---------------------------------------------------------------------- */

.abl-guarantee {
  display: flex; gap: var(--abl-space-3); align-items: flex-start;
  font-size: var(--abl-text-sm);
  line-height: var(--abl-leading-body);
  color: var(--abl-ink-muted);
}
.abl-guarantee svg { flex: 0 0 auto; margin-top: 0.22em; color: var(--abl-success); }
.abl-guarantee strong { color: var(--abl-ink); font-weight: var(--abl-weight-medium); }

/* --- Divider ------------------------------------------------------------ */

/* Inset tray for the wallet buttons. See the note in checkout.html: their colours
   belong to Apple, Link and Amazon and cannot be changed, so the tray contains them
   instead. Slightly recessed and hairline-bordered, so bright brand slabs sit in a
   container that clearly is not ours rather than floating on our panel. */
.abl-express {
  background: var(--abl-surface-inset);
  border: 1px solid var(--abl-line);
  border-radius: var(--abl-radius-md);
  padding: var(--abl-space-3);
}

.abl-divider {
  display: flex; align-items: center; gap: var(--abl-space-4);
  font-size: var(--abl-text-xs);
  color: var(--abl-ink-subtle);
}
.abl-divider::before,
.abl-divider::after {
  content: ""; flex: 1 1 auto; height: 1px; background: var(--abl-line);
}

/* --- Stripe mount points ------------------------------------------------
   Every mount reserves its space before Stripe paints. An element that
   grows after mount shifts everything below it, and layout shift is the
   single most expensive defect on a payment page: redBus took CLS from 1.65
   to 0 and saw an 80 to 100% increase in mobile conversion.

   Note CLS itself will not catch this. Shifts within 500ms of a user input
   are excluded from the metric, and our shifts happen right after a tap, so
   the page can score "good" and still feel broken. Reserve the space anyway.
   ---------------------------------------------------------------------- */

/* The reserve applies only once MOUNTING HAS STARTED. It exists to stop Stripe's
   iframe shoving the page around as it paints, which is a real problem for the
   half-second after mounting begins and a non-problem before that. Applied from
   first paint it did the opposite of its job: it held open big empty boxes on a
   page that had not asked for a state yet, so the reserve WAS the visual defect
   it was added to prevent. */
/* ⚠️ THE :not([data-loaded]) IS LOAD-BEARING. Without it these rules outrank the
   collapse rule below on specificity — one class plus two attributes beats one
   class plus one — so the reserved height NEVER released after Stripe finished
   mounting. That left a ~300px empty box between the payment methods and the pay
   button, which is exactly the defect the reserve exists to prevent, just moved
   later in time. Introduced and found the same day, 2026-07-30. */
.abl-mount { position: relative; }
.abl-mount[data-mounting="true"]:not([data-loaded="true"])[data-reserve="express"]  { min-height: 48px; }
.abl-mount[data-mounting="true"]:not([data-loaded="true"])[data-reserve="contact"]  { min-height: 78px; }
/* Renamed from "shipping" 2026-07-30. Same element slot, same height, different
   element: a BILLING Address Element instead of a shipping one (doc 29 Fix 7).
   The 292px stands because the field set is effectively the same shape.

   The intermediate version of this file moved the 292px onto the payment reserve
   instead, on the theory that a 'required' billing address renders inside the
   Payment Element. It does not, in Custom Checkout. So payment goes back to
   300px and the address keeps its own slot. */
.abl-mount[data-mounting="true"]:not([data-loaded="true"])[data-reserve="address"]  { min-height: 292px; }
.abl-mount[data-mounting="true"]:not([data-loaded="true"])[data-reserve="payment"]  { min-height: 300px; }
.abl-mount[data-loaded="true"]      { min-height: 0; }

/* A skeleton is a LOADING signal, so it must not run before loading starts.
   The Stripe elements cannot mount until a served state is chosen (no state, no
   Checkout Session), and the email mount sits ABOVE the state field in "About
   you". So on first paint the email field shimmered indefinitely and read as a
   broken field rather than a waiting one. Reported from a real staging run
   2026-07-30.

   Now: nothing shimmers until mounting actually begins, and the reserved height
   still holds the space so nothing jumps when it does. */
.abl-skeleton { display: none; }
.abl-mount[data-mounting="true"] .abl-skeleton { display: block; }

.abl-skeleton {
  position: absolute; inset: 0;
  border-radius: var(--abl-radius-md);
  background: linear-gradient(
    100deg,
    rgb(255 241 224 / 0.04) 30%,
    rgb(255 241 224 / 0.09) 50%,
    rgb(255 241 224 / 0.04) 70%
  );
  background-size: 220% 100%;
  animation: abl-shimmer 1.4s var(--abl-ease) infinite;
}
@keyframes abl-shimmer { to { background-position: -220% 0; } }
.abl-mount[data-loaded="true"] .abl-skeleton { display: none; }

.abl-pick { display: grid; gap: var(--abl-space-3); }
@media (min-width: 640px) { .abl-pick { grid-template-columns: 1fr 1fr; } }

.abl-pick__opt {
  display: flex; flex-direction: column; gap: var(--abl-space-2);
  text-align: left; cursor: pointer; font: inherit; color: inherit;
  padding: var(--abl-space-5);
  background: var(--abl-surface-inset);
  border: 1px solid var(--abl-line-strong);
  border-radius: var(--abl-radius-lg);
  transition: border-color var(--abl-speed) var(--abl-ease),
              background-color var(--abl-speed) var(--abl-ease);
}
.abl-pick__opt:hover { border-color: var(--abl-accent); background: var(--abl-accent-wash); }
.abl-pick__opt:focus-visible { outline: none; box-shadow: var(--abl-focus-ring); }

/* Selected state. The picker stays on screen after a choice rather than
   collapsing, so the chosen option has to look chosen: without this the only
   feedback was the summary rail changing, which is easy to miss and leaves
   the two buttons looking equally unpressed. */
.abl-pick__opt[aria-pressed="true"] {
  border-color: var(--abl-accent);
  background: var(--abl-accent-wash);
  box-shadow: inset 0 0 0 1px var(--abl-accent);
}
.abl-pick__opt[aria-pressed="true"] .abl-pick__name { color: var(--abl-accent); }
.abl-pick__opt[aria-pressed="true"] .abl-pick__check { opacity: 1; }
.abl-pick__head { display: flex; align-items: center; justify-content: space-between; gap: var(--abl-space-2); }
.abl-pick__check {
  opacity: 0; color: var(--abl-accent); flex: 0 0 auto;
  transition: opacity var(--abl-speed) var(--abl-ease);
}
.abl-pick__name {
  font-size: var(--abl-text-2xs); font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-wide); text-transform: uppercase;
  color: var(--abl-ink-subtle);
}
.abl-pick__price {
  font-size: var(--abl-text-xl); font-weight: var(--abl-weight-medium);
  letter-spacing: var(--abl-tracking-tight); line-height: 1;
}
.abl-pick__per { font-size: var(--abl-text-sm); color: var(--abl-ink-muted); font-weight: var(--abl-weight-regular); }
.abl-pick__note { font-size: var(--abl-text-sm); color: var(--abl-ink-muted); }

/* --- Blocked state (unsupported location) ------------------------------- */

.abl-blocked {
  border: 1px solid var(--abl-line-accent);
  background: var(--abl-accent-wash);
  border-radius: var(--abl-radius-lg);
  padding: var(--abl-space-6);
}
.abl-blocked[hidden] { display: none; }

/* --- Defence against the site-wide Lenis rule ---------------------------
   The site head custom code contains:

     .lenis-scrolling iframe { pointer-events: none }

   Lenis stamps `.lenis-scrolling` on the html element for the duration of
   smooth-scroll momentum. Every Stripe Element is an iframe, so without this
   override the card fields, the wallet buttons and the address fields are all
   dead to clicks during and just after any scroll. That is a genuine defect
   in the current /store build and it would follow us here.

   Fixed locally rather than by editing the site head, because that rule is
   presumably there for a reason on the marketing pages and this page should
   not be the thing that changes their behaviour. Same specificity as the
   Lenis selector (0,1,1), so !important is what actually wins it.
   ---------------------------------------------------------------------- */

[data-abl] iframe { pointer-events: auto !important; }

/* --- Utilities ---------------------------------------------------------- */

.abl-muted   { color: var(--abl-ink-muted); }
.abl-subtle  { color: var(--abl-ink-subtle); }
.abl-measure { max-width: var(--abl-measure); }
.abl-sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Leave room for the mobile sticky bar so it never covers the pay button. */
@media (max-width: 899px) {
  .abl-shell { padding-bottom: calc(var(--abl-space-16) + 4rem); }
}

/* ---- What-is-included list, summary rail ------------------------------- */
/* Added 2026-07-31. Cade asked the page to state outright what the membership
   buys. Styled as a quiet list, not a marketing feature grid: every line is
   something we are contractually on the hook for, and dressing it up as
   promotional content invites people to skim past it. */
.abl-included {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.4em;
  font-size: 0.9em;
  line-height: 1.45;
  color: hsla(32.9, 100%, 93.92%, 0.85);
}
.abl-included li {
  position: relative;
  padding-left: 1.15em;
}
/* Tick drawn in CSS rather than 5 inline SVGs. Marked aria-hidden by virtue of
   being generated content, so screen readers read the plain list. */
/* Heavier and brighter than the first pass, at JB's request 2026-07-31. The
   original was 1.5px at 0.75 opacity in currentColor, which inherited the muted
   body colour and read as decoration rather than as confirmation. These ticks are
   the only affirmative marks in the rail, so they should carry the accent. */
.abl-included li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.38em;
  width: 0.42em;
  height: 0.24em;
  border-left: 2px solid var(--abl-accent, #F5B54A);
  border-bottom: 2px solid var(--abl-accent, #F5B54A);
  transform: rotate(-45deg);
  opacity: 1;
}

/* ---- Legal footer ------------------------------------------------------ */
/* Deliberately the least prominent text on the page: these are standing
   statements, not terms being agreed to. Small but NOT below 12px, because
   unreadable legal text is functionally undisclosed, which defeats the point of
   having it. The 07-30 lesson on this page was fixed type at mobile widths, so
   this is stated in absolute units and does not inherit a vw-scaled base. */
.abl-legal {
  max-width: 62rem;
  margin: var(--abl-space-16) auto 0;
  padding: 0 var(--abl-space-6) var(--abl-space-16);
  display: grid;
  gap: 0.85rem;
  font-size: 0.75rem;
  line-height: 1.55;
  color: hsla(32.9, 100%, 93.92%, 0.55);
}
.abl-legal p { margin: 0; }
.abl-legal strong {
  color: hsla(32.9, 100%, 93.92%, 0.75);
  font-weight: 500;
}
@media (max-width: 899px) {
  /* Clear the sticky mobile bar, same reason as .abl-shell above. */
  .abl-legal { padding-bottom: calc(var(--abl-space-16) + 4rem); }
}
