/* ==========================================================================
   IX · BUTTONS — hover grammar, shape language, and the consistency fix.

   Variant axis (body[data-rbs-buttons]):
     tint     — the shipped background swap, retimed (enter 220, exit ×0.72)
     sweep    — a fill wipes in from the left behind the label, exits right
     invert   — bg/fg swap; the border never moves, only what it holds
     magnetic — the surface leans toward the cursor, springs back on leave
     corner   — two hairline brackets draw in at opposite corners
     lift     — the surface rises on a soft shadow and its hairline tightens

   Knob attributes written by js/areas/buttons.js apply():
     body[data-rbs-buttons-shape="sharp|rounded|pill"] + --rbs-btn-radius
     body[data-rbs-buttons-label="none|roll"]
     body[data-rbs-buttons-arrow="on|off"]
     body[data-rbs-buttons-unify="on|off"]
   Speed knob: --rbs-btn-dur (studio-written, default 220ms). Per-variant
   knobs: --rbs-btn-mag-lead, --rbs-btn-corner-size, --rbs-btn-corner-inset,
   --rbs-btn-lift, --rbs-btn-lift-shadow.
   Per-element, written by the magnetic tracker: --rbs-btn-mx / --rbs-btn-my
   (0 everywhere else, so every calc() below is an identity outside it) and
   data-rbs-magnet while a button is being followed.

   Everything here is FEEDBACK — it runs at every motion level. Transform-
   based effects (sweep, roll, nudge, pull, lift) are gated to fine pointers
   and prefers-reduced-motion: no-preference; colour, opacity and the corner
   marks still respond everywhere else. With studio JS absent no body
   attribute exists and 05-components ships untouched.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0 · BASE RETIME — all variants. Exits faster than enters; press transform
   joins the transition list so the global :active scale (motion.css) eases
   instead of snapping. Interruptible: transitions retarget mid-flight.
   -------------------------------------------------------------------------- */
body[data-rbs-buttons] .rb-btn {
  transition:
    background-color calc(var(--rbs-btn-dur, 220ms) * 0.72) ease,
    border-color     calc(var(--rbs-btn-dur, 220ms) * 0.72) ease,
    color            calc(var(--rbs-btn-dur, 220ms) * 0.72) ease,
    transform        var(--rb-sys-duration-1) var(--rb-sys-ease-out);
}
body[data-rbs-buttons] .rb-btn:hover {
  transition-duration:
    var(--rbs-btn-dur, 220ms),
    var(--rbs-btn-dur, 220ms),
    var(--rbs-btn-dur, 220ms),
    var(--rb-sys-duration-1);
}

/* --------------------------------------------------------------------------
   1 · TINT — the shipped swap. The retime above IS the variant; the hover
   colours stay exactly as 05-components defined them.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   2 · SWEEP — pseudo-element fill, scaleX 0→1 behind the label.
   transform-origin flips per state: enters from the left edge, exits to the
   right, so re-hover reads as travel, not rewind. Corner distortion during
   the wipe is transient and accepted (standard technique).
   -------------------------------------------------------------------------- */
body[data-rbs-buttons="sweep"] .rb-btn {
  position: relative;
  z-index: 0; /* own stacking context so the -1 fill stays inside the button */
}
body[data-rbs-buttons="sweep"] .rb-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--rbs-btn-sweep-fill, var(--rb-sys-cta-primary-bg-hover));
  transform: scaleX(0);
  transform-origin: 100% 50%; /* rest: collapse toward the right = exit right */
  transition: transform calc(var(--rbs-btn-dur, 220ms) * 1.05) var(--rb-sys-ease-morph);
}

/* Per-tone fill colour. */
body[data-rbs-buttons="sweep"] .rb-btn--primary   { --rbs-btn-sweep-fill: var(--rb-sys-cta-primary-bg-hover); }
body[data-rbs-buttons="sweep"] .rb-btn--secondary { --rbs-btn-sweep-fill: var(--rb-sys-cta-secondary-border); }
body[data-rbs-buttons="sweep"] .rb-btn--dark      { --rbs-btn-sweep-fill: var(--rb-sys-cta-dark-bg-hover); }
body[data-rbs-buttons="sweep"] .rb-btn--accent    { --rbs-btn-sweep-fill: var(--rb-sys-bg-dark); }
body[data-rbs-buttons="sweep"] .rb-btn--ghost     { --rbs-btn-sweep-fill: var(--rb-sys-bg-teal-tint); }

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  body[data-rbs-buttons="sweep"] .rb-btn:hover::before {
    transform: scaleX(1);
    transform-origin: 0% 50%; /* hover: grow from the left edge */
    transition-duration: calc(var(--rbs-btn-dur, 220ms) * 1.45); /* ~320ms */
  }
  /* Neutralise the 05-components hover bg swap — the fill does the work.
     Label colour crossfades where the fill changes the ground under it. */
  body[data-rbs-buttons="sweep"] .rb-btn--primary:hover   { background: var(--rb-sys-cta-primary-bg);   border-color: var(--rb-sys-cta-primary-bg); }
  body[data-rbs-buttons="sweep"] .rb-btn--secondary:hover { background: var(--rb-sys-cta-secondary-bg); color: var(--rb-sys-cta-primary-fg); }
  body[data-rbs-buttons="sweep"] .rb-btn--dark:hover      { background: var(--rb-sys-cta-dark-bg);      border-color: var(--rb-sys-cta-dark-bg); }
  body[data-rbs-buttons="sweep"] .rb-btn--accent:hover    { background: var(--rb-sys-cta-accent-bg);    color: var(--rb-sys-cta-primary-fg); }
  body[data-rbs-buttons="sweep"] .rb-btn--ghost:hover     { background: transparent; }
}

/* Reduced motion: no wipe — the pseudo goes away entirely and the tint
   hover from 05-components takes over (colour still animates, gently). */
@media (prefers-reduced-motion: reduce) {
  body[data-rbs-buttons="sweep"] .rb-btn::before { content: none; }
}

/* --------------------------------------------------------------------------
   3 · INVERT — primary goes outlined, outlined fills. Border colour holds
   its value in every state; only what it contains transitions. Colour-only,
   so it runs ungated (same reach as the shipped hover).
   -------------------------------------------------------------------------- */
body[data-rbs-buttons="invert"] .rb-btn--primary:hover {
  background: var(--rb-sys-cta-secondary-bg);
  color: var(--rb-sys-cta-secondary-fg);
  border-color: var(--rb-sys-cta-secondary-border);
}
body[data-rbs-buttons="invert"] .rb-btn--secondary:hover {
  background: var(--rb-sys-cta-secondary-border);
  color: var(--rb-sys-cta-primary-fg);
  border-color: var(--rb-sys-cta-secondary-border);
}
body[data-rbs-buttons="invert"] .rb-btn--dark:hover {
  background: var(--rb-sys-bg-canvas);
  color: var(--rb-sys-fg-primary);
  border-color: var(--rb-sys-border-strong);
}
body[data-rbs-buttons="invert"] .rb-btn--accent:hover {
  background: var(--rb-sys-bg-canvas);
  color: var(--rb-sys-fg-primary);
  border-color: var(--rb-sys-cta-accent-bg);
}
/* ghost keeps its shipped tint hover — it has nothing to invert. */

/* --------------------------------------------------------------------------
   4 · MAGNETIC — the surface leans toward the cursor. The tracker in
   js/areas/buttons.js writes --rbs-btn-mx / --rbs-btn-my per element
   (clamped to the Pull knob at the button's own edge, vertical damped ×0.6)
   and flips data-rbs-magnet while a button is being followed; everything
   below is composition. One property, two timings:
     followed → 90ms linear, so the lag reads as weight, not delay
     released → 320ms swift, a single settle back into the layout box
   The 05-components colour swap is left alone, so touch and reduced-motion
   users get the shipped feedback with none of the travel.
   -------------------------------------------------------------------------- */
body[data-rbs-buttons="magnetic"] .rb-btn {
  transform: translate3d(var(--rbs-btn-mx, 0px), var(--rbs-btn-my, 0px), 0);
  transition:
    background-color calc(var(--rbs-btn-dur, 220ms) * 0.72) ease,
    border-color     calc(var(--rbs-btn-dur, 220ms) * 0.72) ease,
    color            calc(var(--rbs-btn-dur, 220ms) * 0.72) ease,
    transform        var(--rb-sys-duration-3) var(--rb-sys-ease-swift);
}
body[data-rbs-buttons="magnetic"] .rb-btn[data-rbs-magnet] {
  transition-duration:
    var(--rbs-btn-dur, 220ms),
    var(--rbs-btn-dur, 220ms),
    var(--rbs-btn-dur, 220ms),
    90ms;
  transition-timing-function: ease, ease, ease, linear;
}

/* The label leads the surface by --rbs-btn-mag-lead of the same offset —
   the parallax that keeps the button from reading as one flat sticker. The
   arrow rides along only while the Arrow knob is OFF; with it on, its own
   4px nudge wins (§9, higher specificity) — a larger, deliberate move that
   would only fight this one. */
body[data-rbs-buttons="magnetic"] .rb-btn .rbs-btn-label,
body[data-rbs-buttons="magnetic"] .rb-btn .rbs-btn-arrow {
  transform: translate3d(
    calc(var(--rbs-btn-mx, 0px) * var(--rbs-btn-mag-lead, 0.35)),
    calc(var(--rbs-btn-my, 0px) * var(--rbs-btn-mag-lead, 0.35)), 0);
  transition: transform var(--rb-sys-duration-3) var(--rb-sys-ease-swift);
}
body[data-rbs-buttons="magnetic"] .rb-btn[data-rbs-magnet] .rbs-btn-label,
body[data-rbs-buttons="magnetic"] .rb-btn[data-rbs-magnet] .rbs-btn-arrow {
  transition-duration: 90ms;
  transition-timing-function: linear;
}

/* Press composes with the pull instead of replacing it: motion.css sets a
   bare scale(0.97) on :active at (0,4,1) and loads after this file, so this
   rule carries the extra attribute it needs to win. */
@media (hover: hover) and (pointer: fine) {
  body[data-rbs-buttons="magnetic"][data-rbs-motion-level] .rb-btn:active:not(:disabled) {
    transform: translate3d(var(--rbs-btn-mx, 0px), var(--rbs-btn-my, 0px), 0) scale(0.97);
  }
}

/* --------------------------------------------------------------------------
   5 · CORNER — two hairline brackets draft themselves in at opposite
   corners, top-left first. Each bracket is a pseudo-element carrying two
   borders; the draw is a clip-path inset() growing out of its own corner,
   so both arms extend together the way a drawn mark does. The surface holds
   still — the tint hover is switched off wherever the draw runs, so the
   marks are the entire gesture. Arm height is capped at 30% of the button
   so an --sm button's two brackets can never meet into a full edge.
   Focus-visible draws them too: the keyboard gets the same read.
   -------------------------------------------------------------------------- */
body[data-rbs-buttons="corner"] .rb-btn { position: relative; }
body[data-rbs-buttons="corner"] .rb-btn::before,
body[data-rbs-buttons="corner"] .rb-btn::after {
  content: "";
  position: absolute;
  width: var(--rbs-btn-corner-size, 14px);
  height: min(var(--rbs-btn-corner-size, 14px), 30%);
  border: 0 solid currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity calc(var(--rbs-btn-dur, 220ms) * 0.72) ease;
}
body[data-rbs-buttons="corner"] .rb-btn::before {
  top:  calc(var(--rbs-btn-corner-inset, 4px) + var(--rbs-btn-corner-fit, 0px));
  left: calc(var(--rbs-btn-corner-inset, 4px) + var(--rbs-btn-corner-fit, 0px));
  border-top-width:  var(--rb-ref-border-hairline);
  border-left-width: var(--rb-ref-border-hairline);
}
body[data-rbs-buttons="corner"] .rb-btn::after {
  bottom: calc(var(--rbs-btn-corner-inset, 4px) + var(--rbs-btn-corner-fit, 0px));
  right:  calc(var(--rbs-btn-corner-inset, 4px) + var(--rbs-btn-corner-fit, 0px));
  border-bottom-width: var(--rb-ref-border-hairline);
  border-right-width:  var(--rb-ref-border-hairline);
}
/* A pill curves away from its own bounding corner, so a mark parked there
   would float off the surface onto the page. --corner-fit steps it in to
   roughly where the curve is (≈0.3 × the height of an sm/md pill). Only
   while the pill shape is actually in force: the Shape knob can square
   every .rb-btn--pill on the page, and then the fit must go away. */
body[data-rbs-buttons="corner"]:not([data-rbs-buttons-shape]) .rb-btn--pill::before,
body[data-rbs-buttons="corner"]:not([data-rbs-buttons-shape]) .rb-btn--pill::after,
body[data-rbs-buttons="corner"][data-rbs-buttons-shape="pill"] .rb-btn::before,
body[data-rbs-buttons="corner"][data-rbs-buttons-shape="pill"] .rb-btn::after {
  --rbs-btn-corner-fit: 10px;
}

/* Default reach — touch, coarse pointers, reduced motion: the marks simply
   fade in. Gentler than the draw, never dead. */
body[data-rbs-buttons="corner"] :is(.rb-btn:hover, .rb-btn:focus-visible, .rb-btn.rbs-btn-demo)::before,
body[data-rbs-buttons="corner"] :is(.rb-btn:hover, .rb-btn:focus-visible, .rb-btn.rbs-btn-demo)::after {
  opacity: 1;
  transition-duration: var(--rbs-btn-dur, 220ms);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* The marks are always painted here; the clip is what animates. */
  body[data-rbs-buttons="corner"] .rb-btn::before,
  body[data-rbs-buttons="corner"] .rb-btn::after {
    opacity: 1;
    transition: clip-path calc(var(--rbs-btn-dur, 220ms) * 0.72) var(--rb-sys-ease-swift);
  }
  body[data-rbs-buttons="corner"] .rb-btn::before { clip-path: inset(0 100% 100% 0); }
  body[data-rbs-buttons="corner"] .rb-btn::after  { clip-path: inset(100% 0 0 100%); }

  body[data-rbs-buttons="corner"] :is(.rb-btn:hover, .rb-btn:focus-visible, .rb-btn.rbs-btn-demo)::before,
  body[data-rbs-buttons="corner"] :is(.rb-btn:hover, .rb-btn:focus-visible, .rb-btn.rbs-btn-demo)::after {
    clip-path: inset(0);
    transition-duration: calc(var(--rbs-btn-dur, 220ms) * 1.45);   /* ~320ms draw */
    transition-timing-function: var(--rb-sys-ease-expo);
  }
  /* The second mark trails the first on the way in; both retract together
     and faster, so leaving never feels like an animation played backwards. */
  body[data-rbs-buttons="corner"] :is(.rb-btn:hover, .rb-btn:focus-visible, .rb-btn.rbs-btn-demo)::after {
    transition-delay: 60ms;
  }

  /* Surface holds still — the marks carry the hover. */
  body[data-rbs-buttons="corner"] .rb-btn--primary:hover   { background: var(--rb-sys-cta-primary-bg);   border-color: var(--rb-sys-cta-primary-bg); }
  body[data-rbs-buttons="corner"] .rb-btn--secondary:hover { background: var(--rb-sys-cta-secondary-bg); }
  body[data-rbs-buttons="corner"] .rb-btn--dark:hover      { background: var(--rb-sys-cta-dark-bg);      border-color: var(--rb-sys-cta-dark-bg); }
  body[data-rbs-buttons="corner"] .rb-btn--ghost:hover     { background: transparent; }
}

/* --------------------------------------------------------------------------
   6 · LIFT — the conservative one: a 2px rise on a soft shadow, and the
   hairline tightens to a defined edge. The shadow is a pseudo-element whose
   OPACITY fades (box-shadow itself is never animated), so the elevation
   still arrives under reduced motion where the rise does not. Press drops
   it back to the baseline: motion.css's scale(0.97) replaces the translate,
   which is exactly the read we want from pushing a raised surface down.
   -------------------------------------------------------------------------- */
body[data-rbs-buttons="lift"] .rb-btn {
  position: relative;
  z-index: 0;
  --rbs-btn-lift-edge: var(--rb-sys-border-strong);
}
body[data-rbs-buttons="lift"] .rb-btn--primary { --rbs-btn-lift-edge: var(--rb-sys-bg-dark); }
body[data-rbs-buttons="lift"] .rb-btn--dark    { --rbs-btn-lift-edge: var(--rb-sys-border-on-dark); }
body[data-rbs-buttons="lift"] .rb-btn--accent  { --rbs-btn-lift-edge: var(--rb-sys-fg-primary); }
body[data-rbs-buttons="lift"] .rb-btn--ghost   { --rbs-btn-lift-edge: var(--rb-sys-border-teal); }

body[data-rbs-buttons="lift"] .rb-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  box-shadow: var(--rbs-btn-lift-shadow, var(--rb-ref-shadow-md));
  opacity: 0;
  pointer-events: none;
  transition: opacity calc(var(--rbs-btn-dur, 220ms) * 0.72) ease;
}
/* ghost has no surface to raise — a shadow under it reads as a stray card. */
body[data-rbs-buttons="lift"] .rb-btn--ghost::after { content: none; }

body[data-rbs-buttons="lift"] :is(.rb-btn:hover, .rb-btn.rbs-btn-demo)::after {
  opacity: 1;
  transition-duration: var(--rbs-btn-dur, 220ms);
}
body[data-rbs-buttons="lift"] :is(.rb-btn:hover, .rb-btn.rbs-btn-demo) {
  border-color: var(--rbs-btn-lift-edge);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  body[data-rbs-buttons="lift"] :is(.rb-btn:hover, .rb-btn.rbs-btn-demo) {
    transform: translate3d(0, calc(var(--rbs-btn-lift, 2px) * -1), 0);
  }
}

/* --------------------------------------------------------------------------
   7 · SHAPE — body-level radius override for every .rb-btn. apply() removes
   both the attribute and --rbs-btn-radius for "As designed".
   -------------------------------------------------------------------------- */
body[data-rbs-buttons-shape] .rb-btn {
  border-radius: var(--rbs-btn-radius, var(--rb-ref-radius-xs));
}

/* --------------------------------------------------------------------------
   8 · LABEL ROLL — knob, not variant. init() wraps each button's text as
     .rbs-btn-label > .rbs-btn-label__row > [text + .rbs-btn-label__dup]
   The dup rides the row's single transform (aria-hidden, always). The clip
   window is padded 0.28em and margin-compensated so descenders never crop
   at rest; travel is 165% so nothing lingers in the padded window.
   -------------------------------------------------------------------------- */
.rbs-btn-label       { display: block; }
.rbs-btn-label__row  { display: block; position: relative; }
.rbs-btn-label__dup  { display: none; }

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  body[data-rbs-buttons-label="roll"] .rbs-btn-label {
    overflow: hidden;
    padding: 0.28em 0.1em;
    margin: -0.28em -0.1em;
  }
  body[data-rbs-buttons-label="roll"] .rbs-btn-label__row {
    transition: transform calc(var(--rbs-btn-dur, 220ms) * 1.2) var(--rb-sys-ease-swift);
    transition-delay: 0ms; /* exit: everyone returns together, faster */
  }
  body[data-rbs-buttons-label="roll"] .rbs-btn-label__dup {
    display: block;
    position: absolute;
    top: 165%;
    left: 0;
  }
  body[data-rbs-buttons-label="roll"] .rb-btn:hover .rbs-btn-label__row {
    transform: translateY(-165%);
    transition-duration: calc(var(--rbs-btn-dur, 220ms) * 1.64); /* ~360ms */
    transition-delay: calc(var(--rbs-btn-i, 0) * 15ms); /* stagger per button in a group */
  }
  /* Replay: the studio's Replay button rolls every label in a page-order
     wave (--rbs-btn-n is the page index, set inline by init()). */
  body.rbs-btn-replay[data-rbs-buttons-label="roll"] .rbs-btn-label__row {
    transform: translateY(-165%);
    transition-duration: calc(var(--rbs-btn-dur, 220ms) * 1.64);
    transition-delay: calc(var(--rbs-btn-n, 0) * 45ms);
  }
}

/* --------------------------------------------------------------------------
   9 · ARROW NUDGE — the trailing → (tagged .rbs-btn-arrow at init) slides
   one space unit right on hover. Kept out of the roll wrapper on purpose.
   -------------------------------------------------------------------------- */
body[data-rbs-buttons-arrow="on"] .rbs-btn-arrow {
  transition: transform var(--rb-sys-duration-1) var(--rb-sys-ease-swift);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  body[data-rbs-buttons-arrow="on"] .rb-btn:hover .rbs-btn-arrow {
    transform: translateX(var(--rb-ref-space-1));
    transition-duration: var(--rb-sys-duration-2);
  }
}

/* --------------------------------------------------------------------------
   10 · CONSISTENCY — the nav CTA ships as --dark --rounded --caps while the
   hero pair is --sharp sentence-case. ON pulls it into the primary tone,
   the global shape (falls back to the hero's sharp), and quiet type.
   Kept LAST so its tie-breaks win; per-variant hover overrides below carry
   higher specificity so each hover grammar still reads correctly on it.
   -------------------------------------------------------------------------- */
body[data-rbs-buttons-unify="on"] .rb-nav__cta {
  background: var(--rb-sys-cta-primary-bg);
  border-color: var(--rb-sys-cta-primary-bg);
  color: var(--rb-sys-cta-primary-fg);
  border-radius: var(--rbs-btn-radius, var(--rb-ref-radius-none));
  font-family: var(--rb-sys-font-functional);
  font-weight: var(--rb-ref-weight-medium);
  letter-spacing: normal;
  text-transform: none;
  --rbs-btn-sweep-fill: var(--rb-sys-cta-primary-bg-hover);
}
body[data-rbs-buttons-unify="on"] .rb-nav__cta:hover {
  background: var(--rb-sys-cta-primary-bg-hover);
  border-color: var(--rb-sys-cta-primary-bg-hover);
  color: var(--rb-sys-cta-primary-fg);
}
body[data-rbs-buttons="invert"][data-rbs-buttons-unify="on"] .rb-nav__cta:hover {
  background: var(--rb-sys-cta-secondary-bg);
  color: var(--rb-sys-cta-secondary-fg);
  border-color: var(--rb-sys-cta-secondary-border);
}
/* Lift owns the border on hover — the tighten has to survive the unify tone. */
body[data-rbs-buttons="lift"][data-rbs-buttons-unify="on"] .rb-nav__cta:hover {
  border-color: var(--rbs-btn-lift-edge);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  body[data-rbs-buttons="sweep"][data-rbs-buttons-unify="on"] .rb-nav__cta:hover {
    background: var(--rb-sys-cta-primary-bg);
    border-color: var(--rb-sys-cta-primary-bg);
    color: var(--rb-sys-cta-primary-fg);
  }
  /* Corner holds the surface still on the nav CTA too. */
  body[data-rbs-buttons="corner"][data-rbs-buttons-unify="on"] .rb-nav__cta:hover {
    background: var(--rb-sys-cta-primary-bg);
    border-color: var(--rb-sys-cta-primary-bg);
    color: var(--rb-sys-cta-primary-fg);
  }
}
