/* ==========================================================================
   BUTTON
   Split label + icon-box construction. ONE focusable element.
   Overlay-fade hover, 300ms. No scale, no shadow.
   The focus ring must never be clipped — overflow:hidden lives on the inner
   label block, never on the button itself.
   ========================================================================== */

.btn {
  position: relative; /* the focus ring is drawn from ::before / ::after */
  display: inline-flex;
  align-items: stretch;
  gap: var(--space-2xs);
  border: 0;
  padding: 0;
  background: none;
  border-radius: var(--radius-control);
  font-family: var(--font-family-base);
  font-size: var(--font-size-small);
  line-height: var(--line-height-flat);
  text-align: center;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

/* ==========================================================================
   FOCUS RING — follows the notch
   2px --color-focus-ring at 2px offset (design-rules §18), but shaped to the
   button's real silhouette instead of a plain rounded rect. A straight ring
   past a notched edge reads as the notch biting into the ring.

   TWO PIECES, and it has to be two:
     ::before  the ring itself — a bordered box, so the interior stays
               transparent and the 8px gap between the blocks is untouched.
               `clip-path` removes the wedge, which also removes the border
               along the cut. That is unavoidable: clip-path clips the whole
               rendering, `outline` and `box-shadow: inset` included.
     ::after   re-strokes the wedge the clip just erased. A chevron mask, so
               the stroke takes `background-color` and follows the focus
               token. Sized from --notch-depth / --notch-span, the same two
               values the clip uses, so the two pieces cannot drift.

   The ring's wedge is a translated copy of the button's, not a parallel
   offset. A true 4px offset of a 6×20 wedge widens its mouth to ~59px — it
   would swamp a 52px block. Translating keeps both wedges the same size.

   ONE ring around both blocks, not one per block. Per-block rings were tried
   and reverted: at a 2px offset and 2px width they consume 8px, exactly the
   block gap, so they meet flush and read as a solid bar between the blocks.

   [data-force-focus] is the styleguide's specimen hook. It runs the same
   code path as :focus-visible so a demo can never drift from the real state.
   ========================================================================== */
.btn:focus-visible,
.btn[data-force-focus] { outline: none; }

.btn:focus-visible::before,
.btn[data-force-focus]::before {
  content: "";
  position: absolute;
  inset: calc((var(--focus-ring-width) + var(--focus-ring-offset)) * -1);
  border: var(--focus-ring-width) solid var(--color-focus-ring);
  /* R6x: the ring follows the button's radius, which is now 0.
     The old value was calc(radius + width + offset) — correct for a ROUNDED
     box, where a ring offset outward has a larger corner than the box it
     surrounds. At radius 0 that formula returns 4px and would put rounded
     corners on a ring around a square button. A square offset outward is
     still square, so the ring simply takes the token.
     IF --radius-control EVER RETURNS TO A NON-ZERO VALUE, restore the calc. */
  border-radius: var(--radius-control);
  pointer-events: none;
  clip-path: polygon(
    0 0, 100% 0,
    100% calc(50% - var(--notch-span)),
    calc(100% - var(--notch-depth)) 50%,
    100% calc(50% + var(--notch-span)),
    100% 100%, 0 100%
  );
}

.btn:focus-visible::after,
.btn[data-force-focus]::after {
  content: "";
  position: absolute;
  inset-inline-end: calc((var(--focus-ring-width) + var(--focus-ring-offset)) * -1);
  inset-block-start: 50%;
  inline-size: calc(var(--notch-depth) + var(--focus-ring-width));
  block-size: calc(var(--notch-span) * 2 + var(--focus-ring-width));
  transform: translateY(-50%);
  background-color: var(--color-focus-ring);
  -webkit-mask-image: var(--mask-notch-chevron);
          mask-image: var(--mask-notch-chevron);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  pointer-events: none;
}

/* Forced-colors strips backgrounds, which would take the chevron with it and
   leave the icon block's ring open at the wedge. Hand both rings back to the
   native outline there — straight rings beat a broken one, and the notch is
   cosmetic while the indicator is not. */
@media (forced-colors: active) {
  .btn:focus-visible {
    outline: var(--focus-ring-width) solid;
    outline-offset: var(--focus-ring-offset);
  }
  .btn:focus-visible::before,
  .btn:focus-visible::after { display: none; }
}

/* The label block no longer clips anything — the overlay it existed to clip
   is gone (R5i), and it holds nothing else that overflows. `overflow: hidden`
   was removed with it. It never clipped the focus ring in any case: the ring
   is drawn on .btn, one level up. */
.btn__label {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-height);
  padding-inline: var(--control-padding-inline);
  border-radius: var(--radius-control);
}

.btn__label > span { position: relative; z-index: var(--layer-raised); }

/* NO COLOUR CHANGE ON HOVER — R5i, by direction.
   The overlay fade that used to sit here (and on .btn__icon below) is gone
   from both blocks, along with the --color-overlay-hover token, which had no
   other consumer.

   THE HOVER ITSELF IS NOT GONE. The glyph swap below still fires on every
   button: the resting icon travels out at 50px as the hover icon arrives
   from the left inside a clipped box. That is a larger and more legible
   response than a 14% white tint was, and the more distinctive of the two.

   The :focus-visible ring is untouched — see the block above. The glyph swap
   fires on focus as well as on hover, so the keyboard response is unchanged
   and §18 still holds. What was removed was decoration on top of the
   accessible indicator, never the indicator. */

/* THE NOTCH IS THE BUTTON.
   Every button carries it — primary, secondary and plate alike. There is no
   un-notched variant and no modifier to add one.

   A §12.1 rectangular subtraction on the trailing edge of the icon block, so
   the notch reads as a bite out of the button's outer edge.

   `border-radius` rounds the background BEFORE `clip-path` removes the wedge,
   so any radius survives intact and only the notch is cut. That mattered when
   the corners were 8px (R6x squared them); the mechanism is left described
   because it is what makes the notch safe at ANY radius, not just at 0. The focus ring lives on .btn, one level up, and a
   child's clip-path cannot reach a parent's outline — so the ring is never
   clipped (design-rules §18). */
/* `overflow: hidden` STAYS on this one, and it is load-bearing: it is what
   clips the glyph swap, so the icon reads as replaced rather than as sliding
   out of the button. */
.btn__icon {
  position: relative;
  overflow: hidden;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--icon-box-size);
  min-height: var(--control-height);
  border-radius: var(--radius-control);
  clip-path: polygon(
    0 0, 100% 0,
    100% calc(50% - var(--notch-span)),
    calc(100% - var(--notch-depth)) 50%,
    100% calc(50% + var(--notch-span)),
    100% 100%, 0 100%
  );
}

/* ==========================================================================
   SHIMMER — R25g, by direction: "every 3 seconds the sticky 'Request For Your
   Seat' CTA button will have a shimmering effect."

   ⚠⚠ AN ELEMENT, NOT A PSEUDO-ELEMENT, AND THAT IS FORCED. .btn's ::before
   and ::after are BOTH the focus ring (the ring and the wedge that re-strokes
   its notch), so a sheen on either would fight the one indicator §18
   requires. A child span is free of that, and its presence in the markup is
   also the switch: the two sticky CTAs carry it, the page's other five
   buttons do not, and no modifier class has to stay in step with a list.

   ⚠⚠ THE CLIP IS ON THE SPAN, NEVER ON .btn. This file's opening rule says
   it: "the focus ring must never be clipped — overflow:hidden lives on the
   inner label block, never on the button itself." The span is inset: 0 inside
   the button and clips its own child, so the band is contained without any
   clipping reaching the ring one level up.

   ⚠ IT PAINTS ABOVE THE LABEL AND THE GLYPH BY DOM ORDER, not by a higher
   layer: both carry --layer-raised, and at equal z-index the later sibling
   wins. Nothing here needs a --layer- token of its own. .btn__icon's
   clip-path makes it a stacking context at z-index auto, so the band clears
   that too.

   ⚠ pointer-events: none — a sheet over the whole control would otherwise
   swallow the click on the page's only conversion point.

   ⚠⚠ WCAG 2.2.2, AND IT IS A KNOWN COST. This starts automatically, runs for
   longer than five seconds, and has no pause control. CLAUDE.md §10 already
   records four such elements as an open defect — the Hero slider, the Hero
   marquee, §11's rails and §9's photo rail. This is the FIFTH, and unlike
   those it sits on the conversion control itself. It stops for anyone with
   `prefers-reduced-motion` (below), which is the only mitigation in place.
   ========================================================================== */
.btn__shimmer {
  position: absolute;
  inset: 0;
  z-index: var(--layer-raised);
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

/* The band itself. 40% of the control's width, travelling from clear of one
   edge to clear of the other, so it never sits parked inside the button.

   ⚠⚠ IT OVERHANGS TOP AND BOTTOM, AND THE SKEW IS WHY. Skewing a box shears
   it: the top and bottom edges slide apart by height × tan(angle), so a band
   flush with the control would leave an unlit wedge at one corner for the
   whole crossing. -25% on each edge covers the shear with room to spare, and
   .btn__shimmer's own overflow clips it back to the button. */
.btn__shimmer::before {
  content: "";
  position: absolute;
  inset-block: -25%;
  inline-size: 40%;
  background-image: var(--gradient-shimmer);

  /* ⚠⚠ INVISIBLE AT REST, AND NOT ONLY BY BEING OFF-SCREEN — R25k, by
     direction: "the button will be clean on page load and the shimmer
     gradient only appears after 1 second." Parking the band outside the
     clip was the whole guarantee before, which made a visible band the
     failure mode of ANY mistake in the travel. Opacity is a second,
     independent guarantee: with the animation not running — during its
     delay, and under prefers-reduced-motion — there is nothing to see.
     The keyframes raise it only while the band is crossing. */
  opacity: 0;

  /* ⚠⚠ THE TRAVEL AND THE LEAN ARE ON DIFFERENT PROPERTIES ON PURPOSE.
     `translate` is its own property; `transform` carries only the static
     skew. Written as one `transform: translateX(...) skewX(var(...))`, an
     unresolvable --shimmer-skew makes the WHOLE declaration invalid at
     computed-value time, the translate goes with it, and the band lands
     square in the middle-left of the button — the exact symptom this
     revision was reported for. Split, a failed skew costs the lean and
     nothing else. */
  translate: -160%;
  transform: skewX(var(--shimmer-skew));
}

/* ⚠ THE ANIMATION IS GATED ON no-preference, AND THE GATE HAS TO BE HERE.
   base.css forces transition-duration to 0.01ms globally under `reduce` —
   that reaches transitions, NOT animations. An unguarded keyframe loop would
   run at full speed for a reader who asked for no motion. The resting state
   above leaves the band off-screen, so switching the animation off leaves a
   plain button rather than a stripe parked across the label. */
/* ⚠ TWO TIME VALUES IN THE SHORTHAND, AND ORDER DECIDES WHICH IS WHICH:
   duration first, delay second. Swapped, this would silently run a 1s cycle
   after a 2s wait — the same two numbers, the wrong way round. */
@media (prefers-reduced-motion: no-preference) {
  .btn__shimmer::before {
    animation: btn-shimmer var(--duration-shimmer) var(--ease-standard) var(--delay-shimmer) infinite;
  }
}

/* The sweep occupies --duration-shimmer-travel of the cycle and the band
   waits off-screen for the rest; 50% of 2s is 1000ms. The two stops after the
   travel are what make the pause, so the percentage and the token have to
   agree — change one and the band either stutters or never rests.

   ⚠ THE SKEW IS NO LONGER REPEATED HERE, and that is the point of moving the
   travel onto `translate`: the lean sits on `transform`, untouched by these
   stops, so it cannot animate back to zero and cannot be lost if a stop is
   edited carelessly.

   ⚠ OPACITY RIDES WITH THE TRAVEL. It is 1 only while the band crosses and
   drops at 50%, where the band is already clear of the right edge — so the
   fade is never seen, and the band cannot show during the pause. */
@keyframes btn-shimmer {
  0%   { translate: -160%; opacity: 1; }
  50%  { translate: 260%;  opacity: 1; }
  51%  { translate: 260%;  opacity: 0; }
  100% { translate: 260%;  opacity: 0; }
}

/* ICON SWAP
   [Config] Two icon copies inside one overflow-hidden box. The hover copy
   rests at translate3d(-50px,0,0); on hover the resting glyph travels out to
   the right and the hover glyph arrives from the left. Because the box clips,
   it reads as the icon being REPLACED rather than nudged — which is what
   separates it from the arrow-shifts on every other event page.
   Mechanism is Config's; the 50px, the 300ms and the easing are ours. */
.btn__glyph {
  position: relative;
  z-index: var(--layer-raised);
  inline-size: var(--icon-size-pixel);
  block-size: var(--icon-size-pixel);
  /* Filled pixel glyph on a 15-unit grid, rendered at 18px.
     `shape-rendering: crispEdges` is deliberately omitted: 18 is not a whole
     multiple of 15, so snapping would round alternate steps to different
     widths and break the grid illusion. Default anti-aliasing keeps every
     step proportionally equal at the cost of a hair of softness, which is
     the better trade at this size. Re-enable crispEdges only if the size
     returns to an exact multiple (15px or 30px). */
  fill: currentColor;
  transition: transform var(--duration-base) var(--ease-standard);
}

.btn__glyph--hover {
  position: absolute;
  transform: translateX(calc(var(--icon-swap-distance) * -1));
}

.btn:hover .btn__glyph        { transform: translateX(var(--icon-swap-distance)); }
.btn:hover .btn__glyph--hover { transform: none; }

/* Keyboard focus gets the same feedback as the pointer. */
.btn:focus-visible .btn__glyph        { transform: translateX(var(--icon-swap-distance)); }
.btn:focus-visible .btn__glyph--hover { transform: none; }

/* No hover on touch — the resting glyph must never travel out and leave the
   box empty. */
@media (hover: none) {
  .btn__glyph--hover { display: none; }
  .btn:hover .btn__glyph { transform: none; }
}

/* --------------------------------------------------------------------------
   PRIMARY
   --color-control-primary ground · --color-on-action label.

   VIOLET, by direction, and it is the tightest pairing on the page:
   #865BFF + #0F0F0F = 4.52:1, clearing AA for normal text by 0.02.
   NEVER white on this ground — white is 4.24:1 and fails outright.

   This overrides the standing "violet is never a control" rule; the reason
   and the measurements are recorded in design-rules §6 and in tokens.css.
   Weight 700 — one of only two permitted uses of bold.
   -------------------------------------------------------------------------- */
.btn--primary .btn__label,
.btn--primary .btn__icon {
  background-color: var(--color-control-primary);
  color: var(--color-on-action);
  font-weight: var(--font-weight-bold);
}

/* --------------------------------------------------------------------------
   SOLID — un-notched, chip icon. R5p, promoted to a modifier at R6e.

   THIS IS THE MODIFIER §9 SAID COULD NOT EXIST: "there is no un-notched
   variant and no modifier to add one." It was scoped to .hero__cta at R5p
   precisely so it could not spread. The header then asked for the same
   treatment, and a second copy of the rules would have been worse than the
   modifier — two places to keep in step, one of them certain to drift.

   Used by: the Hero CTA · the header CTA. The modal, the §7 plate and the
   Final CTA still carry the notch. Two shapes for one control is still an
   inconsistency; it is now a two-against-three split rather than one-against-
   four. See design-rules R5p and R6e.

   THE WHITE LABEL IS NOT A CHOICE — it is the only legal option on this
   ground, and it inverts the standing rule:
     #6146ED + #FFFFFF = 5.82:1  ✓
     #6146ED + #0F0F0F = 3.29:1  ✗  ← the near-black label CANNOT come here
     #865BFF + #FFFFFF = 4.24:1  ✗  ← which is why the rule says "never white"
   "Never white on violet" holds for #865BFF only. The two violets are not
   interchangeable; this is the second reason after R5j.

   HELD AGAINST THE REFERENCE, deliberately: 8px radius — a sixth radius value
   is a harder limit than the notch was.
   -------------------------------------------------------------------------- */
.btn--solid {
  gap: 0;                                    /* the blocks close up */
  padding: var(--space-2xs);                 /* 8 — the inset the chip sits in */
  align-items: center;
  background-color: var(--color-accent-identity-deep);
  border-radius: var(--radius-control);
}

/* The label loses its own ground and radius — the button carries both now.
   Its height comes off --control-height minus the padding, so the total is
   still exactly 52px.
   WEIGHT 400, R5s — down from the 700 §5 assigns the primary CTA label. It
   adds no weight to the page's four; it gives up the CTA reading as the
   heaviest element in its section, which the violet ground carries instead. */
.btn--solid .btn__label {
  background-color: transparent;
  color: var(--color-text-heading);
  font-weight: var(--font-weight-regular);
  min-height: calc(var(--control-height) - (var(--space-2xs) * 2));
  padding-inline: var(--space-md);
  border-radius: 0;
}

/* THE CHIP. Square, white, inset. `clip-path: none` is what removes the
   notch — the polygon is declared on .btn__icon and has to be cancelled, not
   merely covered. It keeps overflow:hidden, so the glyph swap still clips. */
.btn--solid .btn__icon {
  clip-path: none;
  inline-size: calc(var(--control-height) - (var(--space-2xs) * 2));
  min-height: calc(var(--control-height) - (var(--space-2xs) * 2));
  background-color: var(--color-text-heading);
  color: var(--color-on-action);             /* 19.2:1 on white */
  border-radius: var(--radius-control);
}

/* THE ARROW'S STATIC SHAPE — R5t.
   The supplied glyph's second path carries stroke-dashoffset="12" as a
   presentation attribute, which leaves the arrowhead only partly drawn until
   the SMIL animation resolves it. If SMIL does not run — a hardened browser,
   a strict CSP, an SVG sanitiser — the arrow renders permanently broken.

   CSS beats a presentation attribute, so this pins the resolved state as the
   floor. It cannot suppress the animation where SMIL does run: <animate>
   overrides CSS as well as attributes. See design-rules R5t. */
.btn--solid .btn__glyph path { stroke-dashoffset: 0; }

/* THE FOCUS RING GOES BACK TO A PLAIN ROUNDED RECT.
   Both pieces have to be dealt with: the ::before clip is cancelled, and the
   ::after chevron is removed outright. Leaving the chevron would re-stroke a
   wedge that no longer exists — a green notch floating off the right edge. */
.btn--solid:focus-visible::before,
.btn--solid[data-force-focus]::before { clip-path: none; }

.btn--solid:focus-visible::after,
.btn--solid[data-force-focus]::after { display: none; }

/* --------------------------------------------------------------------------
   INK — R8t. The --solid button on a near-black ground. Ground only: the
   layout it briefly carried at R8u was reverted at R8x.

   THE REFERENCE BUTTON IS ALREADY IN THE SYSTEM. What was supplied — dark
   ground, white label, contrasting icon chip, full width — is exactly
   .btn--solid, which the Hero CTA uses; the only difference is the ground.
   So this is one declaration, not a new button.

   ⚠ IT IS THE PAGE'S SECOND INVERTED BUTTON. §5 says the plate exception is
   "section 7 only ... the only inverted button on the page". This makes two.
   The reason is the same arithmetic that produced the first: violet on the
   #F5F5F5 answer panel would be a violet button on a light ground with a
   near-black label at 4.52:1 — legal, but it puts the section's dominant
   accent on a control that is not the page's primary action, three inches
   below four violet ✓ badges. Ink separates them.

   MEASURED: white label on #0F0F0F is 19.17:1 · the #0F0F0F glyph on the
   white icon chip is 19.17:1 · the button as a shape on the panel is 17.58:1.

   R8u — THE GLYPH IS THE HERO'S, by direction, replacing the pixel arrow.
   That makes §4's CTA and the Hero's CTA the same control end to end: same
   component, same variant construction, same mark. It adds no NEW icon
   language — the SMIL arrow already ships in the Hero.

   ⚠ IT INHERITS THE SMIL PROBLEM (R5t). <animate> overrides both presentation
   attributes and CSS, so no media query can stop it: the arrow draws itself
   on load for every visitor, including under prefers-reduced-motion. There
   are now TWO instances of this on the page rather than one. The static
   fallback holds — .btn--solid .btn__glyph path pins stroke-dashoffset: 0, so
   the correct shape renders wherever SMIL does not run — but the animation
   itself still cannot be suppressed. The fix remains the CSS-keyframes swap
   described at R5t.
   -------------------------------------------------------------------------- */
/* R8x — R8u's pair-centring is REVERTED, by direction. The chip goes back to
   the right edge, which is .btn--block's own behaviour: `flex: 1 1 auto` on
   the label fills the bar and pushes the icon block to the end, and the label
   centres its text inside the space that leaves.

   Nothing scoped remains here — the two declarations that produced the
   centred pair (justify-content on the button, a flex override on the label)
   are gone rather than neutralised, so this variant is a GROUND modifier
   again and carries no layout of its own. The Hero glyph stays. */
.btn--ink { background-color: var(--color-on-plate-invert); }

/* --------------------------------------------------------------------------
   INVERT — R23c
   The primary button with its two blocks swapped: white label block, violet
   icon chip. Built to a supplied reference for §12's violet plate, where
   .btn--solid's own ground IS the section's ground.

   A FOURTH GROUND MODIFIER after --solid, --ink and the plate inversion.
   It carries no geometry — --solid still supplies the closed-up blocks, the
   8px inset and the cancelled notch. Only the four colours change.

   ✅ IT CONSUMES --color-surface-invert, which has had NO CONSUMER since
   R18k took it off §7's eyebrow. The page already owned this white.

   MEASURED, all four pairings:
     white ground on §12's plate       7.12:1 desktop · 7.82:1 mobile
     --color-on-action on white       19.20:1
     violet chip on white              4.95:1   (non-text, needs 3)
     white arrow on --identity-deep    5.82:1

   ⚠⚠ THE CHIP IS --color-accent-identity-deep AND NOT --color-accent-
   identity. White on #6146ED is 5.82:1; white on #865BFF is 4.24:1 and is
   prohibited page-wide. #865BFF is the one that reads as "purple" at a
   glance and it is the one that fails. Fourth place this has mattered.

   ✅ It also closes R23b's open WCAG 1.4.11 shortfall: --ink gave this
   button a 2.69:1 / 2.45:1 boundary on the plate.
   -------------------------------------------------------------------------- */
.btn--invert { background-color: var(--color-surface-invert); }

.btn--invert .btn__label { color: var(--color-on-action); }

.btn--invert .btn__icon {
  background-color: var(--color-accent-identity-deep);
  color: var(--color-text-heading);
}

/* The ring follows the ground it lands on, the precedent --ink set. Green on
   white is 1.21:1 and cannot be the indicator here; --color-on-action is
   19.2:1 on the button and the 2px offset keeps a band of plate showing, so
   it still reads as a ring rather than a fatter button. */
.btn--invert:focus-visible::before,
.btn--invert[data-force-focus]::before { border-color: var(--color-on-action); }

.btn--invert:focus-visible::after,
.btn--invert[data-force-focus]::after { background-color: var(--color-on-action); }

/* ⚠ THE FOCUS RING CANNOT STAY GREEN HERE. §18 fixes it at accent-confirm,
   2px at 2px offset — and #3BFF9A on the #F5F5F5 panel measures 1.21:1,
   far under the 3:1 an indicator needs. White is worse at 1.09:1.

   --color-on-plate-invert is 17.58:1 on the panel, and the 2px offset keeps a
   band of panel visible between button and ring so it reads as a ring rather
   than a fatter button. This follows the precedent the plate button set: the
   ring takes whatever reads on the ground it lands on, and stays
   distinguished from the ✓ marker by GEOMETRY — a 2px outline is a form no
   content element on this page takes. */
.btn--ink:focus-visible::before,
.btn--ink[data-force-focus]::before { border-color: var(--color-on-plate-invert); }

.btn--ink:focus-visible::after,
.btn--ink[data-force-focus]::after { background-color: var(--color-on-plate-invert); }

/* --------------------------------------------------------------------------
   MODIFIERS
   -------------------------------------------------------------------------- */

/* Full width. Primary CTA is full-width on mobile. */
.btn--block { display: flex; width: 100%; }
.btn--block .btn__label { flex: 1 1 auto; }

/* Disabled suppresses the glyph swap. It used to have to suppress the hover
   overlay too; with the overlay gone (R5i) the travel is the only thing left
   that could still fire on a dead control. */
.btn:disabled,
.btn[aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; }

.btn:disabled:hover .btn__glyph,
.btn[aria-disabled="true"]:hover .btn__glyph { transform: none; }

.btn:disabled:hover .btn__glyph--hover,
.btn[aria-disabled="true"]:hover .btn__glyph--hover {
  transform: translateX(calc(var(--icon-swap-distance) * -1));
}

@media (max-width: 767px) {
  /* Primary CTA is full-width on mobile (design-rules §9). */
  .btn--primary { display: flex; width: 100%; }
  .btn--primary .btn__label { flex: 1 1 auto; }
}
