/* ============================================================
   RaceLink user-button manual — site-themed component
   Designed to drop into racelink.dev (Eleventy) inside a
   ``.section > .wrap`` or ``.prose`` container.

   Inherits all design tokens from the site stylesheet:
     --bg --bg-2 --surface --surface-2
     --text --muted --line --line-strong
     --cyan --pink --amber --grad
   ============================================================ */

.rl-buttons{
  /* Indicator semantics — mirror RaceLink_WLED/racelink_indicators.h. */
  --rl-strobe-reject:#ff3a3a;          /* IND_PROBE_REJECTED  */
  --rl-strobe-enter: var(--cyan);      /* IND_HEADLESS_ENTER  */
  --rl-strobe-exit:  var(--amber);     /* IND_HEADLESS_EXIT   */
  --rl-scene:        #39d28b;          /* scene step          */
  --rl-bri:          var(--amber);     /* brightness fade     */

  /* Colour-cycle vivid stops — for the standalone single-click
     R → G → B → random demo. */
  --rl-cycle-r:#ff4d4d;
  --rl-cycle-g:#39d28b;
  --rl-cycle-b:#5ea7ff;
  --rl-cycle-rand:var(--pink);

  --rl-action:   var(--cyan);
  --rl-led-idle: rgba(255,255,255,.12);

  /* Cycle length — one source of truth for every animation. */
  --rl-cycle: 4s;

  display:flex;
  flex-direction:column;
  gap:clamp(28px,4vw,42px);
  margin:42px 0;
  color:var(--text);
}

/* ============================================================
   Mode panel
   ============================================================ */

.rl-buttons__mode{
  background:linear-gradient(180deg,var(--surface),var(--bg-2));
  border:1px solid var(--line);
  border-radius:18px;
  padding:clamp(22px,3vw,32px) clamp(22px,3vw,30px);
}

.rl-buttons__mode-head{
  margin-bottom:clamp(18px,2.5vw,26px);
}

.rl-buttons__mode-title{
  font-size:clamp(22px,2.4vw,28px);
  font-weight:700;
  line-height:1.15;
  margin:0 0 10px;
}

.rl-buttons__mode-context{
  margin:0;
  color:var(--muted);
  font-size:15.5px;
  line-height:1.6;
  max-width:62ch;
}
.rl-buttons__mode-context em{
  font-style:normal;
  color:var(--text);
  font-weight:600;
}

/* ============================================================
   Gesture row
   ============================================================ */

.rl-buttons__row{
  display:grid;
  grid-template-columns:150px 240px 1fr;
  align-items:center;
  gap:18px;
  padding:18px 0;
  border-top:1px solid var(--line);
}
.rl-buttons__row:first-of-type{
  margin-top:clamp(14px,2vw,18px);
}

.rl-buttons__label{
  display:flex;
  flex-direction:column;
  gap:2px;
}
.rl-buttons__label strong{
  font-family:var(--font-display);
  font-weight:600;
  font-size:16px;
  color:var(--text);
  letter-spacing:-.005em;
}
.rl-buttons__label span{
  font-family:var(--font-display);
  font-weight:500;
  font-size:11.5px;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--muted);
}

.rl-buttons__visual{
  display:flex;
  align-items:center;
  gap:10px;
}

.rl-buttons__arrow{
  flex:0 0 22px;
  height:1px;
  background:var(--line-strong);
  position:relative;
}
.rl-buttons__arrow::after{
  content:"";
  position:absolute;
  right:-1px;
  top:50%;
  width:6px;
  height:6px;
  border-top:1px solid var(--muted);
  border-right:1px solid var(--muted);
  transform:translateY(-50%) rotate(45deg);
}

.rl-buttons__caption{
  margin:0;
  color:var(--muted);
  font-size:15px;
  line-height:1.6;
}
.rl-buttons__caption strong{
  color:var(--text);
  font-weight:600;
}
.rl-buttons__caption code{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:.85em;
  padding:1px 6px;
  background:rgba(255,255,255,.04);
  border:1px solid var(--line);
  border-radius:5px;
  color:var(--text);
}

/* ============================================================
   Footer note
   ============================================================ */

.rl-buttons__footer{
  display:flex;
  flex-direction:column;
  gap:6px;
  padding:18px 22px;
  background:rgba(255,255,255,.02);
  border:1px solid var(--line);
  border-radius:14px;
  color:var(--muted);
  font-size:14.5px;
  line-height:1.6;
}
.rl-buttons__footer p{margin:0;}
.rl-buttons__footer strong{color:var(--text);font-weight:600;}

/* ============================================================
   Responsive — stack at narrow widths
   ============================================================ */

@media (max-width:640px){
  .rl-buttons__row{
    grid-template-columns:1fr;
    gap:10px;
  }
  .rl-buttons__label{
    flex-direction:row;
    align-items:baseline;
    gap:14px;
  }
}

/* ============================================================
   SVG primitives — no text, just animated mechanics
   ============================================================ */

.rl-input,
.rl-output{
  display:block;
  flex:0 0 auto;
  height:38px;
  overflow:visible;
}
.rl-input  { width:92px; }
.rl-output { width:110px; color:var(--muted); }

.rl-input__button{
  fill:var(--bg-2);
  stroke:var(--line-strong);
  stroke-width:1;
}
.rl-input__button-inner{
  fill:var(--muted);
  opacity:.45;
}

.rl-input__pip,
.rl-input__hold-bar,
.rl-output__led{
  fill:var(--rl-led-idle);
}

.rl-output__body{
  fill:rgba(255,255,255,.02);
  stroke:var(--line);
  stroke-width:1;
}

.rl-output__icon{
  opacity:0;
  transform-origin:55px 18px;
  transform-box:fill-box;
}

/* ============================================================
   Input animations — single 4 s cycle, shared phase

   Timeline (% of --rl-cycle):
     0–24%   idle
     25–X%   click(s) happen
       single: 25–32%
       triple: 25–28, 33–36, 41–44
       five:   25–27, 31–33, 37–39, 43–45, 49–51
       hold:   pressed 20–70%
     reaction window then fires AFTER the last release + a brief
     multi-tap pause; see "Reactions" block below.
   ============================================================ */

@keyframes rl-press-single{
  0%,   24%  {fill:var(--bg-2);     transform:scale(1);}
  25%,  32%  {fill:var(--rl-action);transform:scale(.86);}
  33%, 100%  {fill:var(--bg-2);     transform:scale(1);}
}
@keyframes rl-press-triple{
  0%,   24%  {fill:var(--bg-2);     transform:scale(1);}
  25%,  28%  {fill:var(--rl-action);transform:scale(.86);}
  29%,  32%  {fill:var(--bg-2);     transform:scale(1);}
  33%,  36%  {fill:var(--rl-action);transform:scale(.86);}
  37%,  40%  {fill:var(--bg-2);     transform:scale(1);}
  41%,  44%  {fill:var(--rl-action);transform:scale(.86);}
  45%, 100%  {fill:var(--bg-2);     transform:scale(1);}
}
@keyframes rl-press-five{
  0%,   24%  {fill:var(--bg-2);     transform:scale(1);}
  25%,  27%  {fill:var(--rl-action);transform:scale(.86);}
  28%,  30%  {fill:var(--bg-2);     transform:scale(1);}
  31%,  33%  {fill:var(--rl-action);transform:scale(.86);}
  34%,  36%  {fill:var(--bg-2);     transform:scale(1);}
  37%,  39%  {fill:var(--rl-action);transform:scale(.86);}
  40%,  42%  {fill:var(--bg-2);     transform:scale(1);}
  43%,  45%  {fill:var(--rl-action);transform:scale(.86);}
  46%,  48%  {fill:var(--bg-2);     transform:scale(1);}
  49%,  51%  {fill:var(--rl-action);transform:scale(.86);}
  52%, 100%  {fill:var(--bg-2);     transform:scale(1);}
}
@keyframes rl-press-hold{
  0%,   19%  {fill:var(--bg-2);     transform:scale(1);}
  20%,  70%  {fill:var(--rl-action);transform:scale(.86);}
  71%, 100%  {fill:var(--bg-2);     transform:scale(1);}
}

.rl-buttons__row .rl-input__button{
  transform-origin:20px 18px;
  animation-duration:var(--rl-cycle);
  animation-iteration-count:infinite;
  animation-timing-function:steps(1,end);
}
.rl-buttons__row[data-gesture="single"] .rl-input__button{animation-name:rl-press-single;}
.rl-buttons__row[data-gesture="triple"] .rl-input__button{animation-name:rl-press-triple;}
.rl-buttons__row[data-gesture="five"]   .rl-input__button{animation-name:rl-press-five;}
.rl-buttons__row[data-gesture="hold"]   .rl-input__button{animation-name:rl-press-hold;}

/* ---- pip blink — staggered per pip via --pip-i ---- */

@keyframes rl-pip-blink{
  0%,99%,100%{fill:var(--rl-led-idle);}
  4%, 32%    {fill:var(--rl-action);}
}
.rl-buttons__row .rl-input__pip{
  animation:rl-pip-blink var(--rl-cycle) infinite steps(1,end);
}
.rl-buttons__row[data-gesture="single"] .rl-input__pip{
  animation-delay:calc(var(--rl-cycle) * .25);
}
.rl-buttons__row[data-gesture="triple"] .rl-input__pip{
  animation-delay:calc(var(--rl-cycle) * (.25 + var(--pip-i) * .08));
}
.rl-buttons__row[data-gesture="five"] .rl-input__pip{
  animation-delay:calc(var(--rl-cycle) * (.25 + var(--pip-i) * .06));
}

/* ---- hold bar grows during the hold window ---- */

@keyframes rl-hold-bar{
  0%,   19%  {width:6px; fill:var(--rl-led-idle);}
  20%,  70%  {width:38px;fill:var(--rl-action);}
  71%, 100%  {width:6px; fill:var(--rl-led-idle);}
}
.rl-buttons__row[data-gesture="hold"] .rl-input__hold-bar{
  animation:rl-hold-bar var(--rl-cycle) infinite steps(1,end);
}

/* ============================================================
   Reactions — what the LED strip does

   Timing rule: a reaction NEVER overlaps its triggering click(s).
   Each window starts AFTER the gesture's last release + a short
   pause (suggests the firmware's multi-tap window — real value
   ~600 ms; here ~400–440 ms = 10–11 % of the cycle).

   Per-gesture reaction windows on the shared 4 s cycle:
     single (release  32 %) → 36–85 %
     triple (release  44 %) → 56–92 %
     five   (release  51 %) → 62–95 %
     hold   (held 20–70 %)  → 22–68 % during, 71–78 % post-release

   Icons fade AND scale-in (0.6 → 1.0) so the broadcasting /
   blocked gesture reads clearly even on a static screenshot.
   ============================================================ */

/* ---- GATED — single+gated (Mode 1) and hold+gated (Mode 1) ---- */

.rl-buttons__row[data-reaction="gated"] .rl-output__led{
  opacity:.55;
}

@keyframes rl-icon-gated-after-single{
  0%, 34%, 86%, 100%  {opacity:0;transform:scale(.6);}
  38%, 82%            {opacity:.95;transform:scale(1);}
}
@keyframes rl-icon-gated-during-hold{
  0%, 21%, 69%, 100%  {opacity:0;transform:scale(.6);}
  24%, 65%            {opacity:.95;transform:scale(1);}
}
.rl-buttons__row[data-gesture="single"][data-reaction="gated"] .rl-output__icon--gated{
  animation:rl-icon-gated-after-single var(--rl-cycle) infinite;
  color:var(--muted);
}
.rl-buttons__row[data-gesture="hold"][data-reaction="gated"] .rl-output__icon--gated{
  animation:rl-icon-gated-during-hold var(--rl-cycle) infinite;
  color:var(--muted);
}

/* ---- AP — triple+ap (all three modes) ---- */

@keyframes rl-led-ap-after-triple{
  0%, 55%, 93%, 100%  {fill:var(--rl-led-idle);}
  56%, 92%            {fill:color-mix(in srgb,var(--cyan) 32%,transparent);}
}

/* Each wifi element has its own keyframe. They share the same
   fade-out window (88–92%) but stagger their fade-in by ~5%
   each (≈ 200 ms on a 4 s cycle), producing the "wave radiates
   outward" effect. */
@keyframes rl-ap-source{
  0%, 55%, 92%, 100%  {opacity:0;}
  58%, 88%            {opacity:1;}
}
@keyframes rl-ap-wave-1{
  0%, 60%, 92%, 100%  {opacity:0;}
  63%, 88%            {opacity:1;}
}
@keyframes rl-ap-wave-2{
  0%, 65%, 92%, 100%  {opacity:0;}
  68%, 88%            {opacity:1;}
}
@keyframes rl-ap-wave-3{
  0%, 70%, 92%, 100%  {opacity:0;}
  73%, 88%            {opacity:1;}
}

.rl-buttons__row[data-gesture="triple"][data-reaction="ap"] .rl-output__led{
  animation:rl-led-ap-after-triple var(--rl-cycle) infinite steps(1,end);
}

/* The wifi parent group no longer animates — children manage
   their own visibility. We just unlock the parent (the default
   ``.rl-output__icon`` rule sets opacity:0 to keep icons hidden
   on rows that don't need them) and apply the brand colour. */
.rl-buttons__row[data-gesture="triple"][data-reaction="ap"] .rl-output__icon--ap{
  opacity:1;
  color:var(--cyan);
}

.rl-buttons__row[data-gesture="triple"][data-reaction="ap"] .rl-output__icon-ap-source{
  animation:rl-ap-source var(--rl-cycle) infinite;
}
.rl-buttons__row[data-gesture="triple"][data-reaction="ap"] .rl-output__icon-ap-wave--1{
  animation:rl-ap-wave-1 var(--rl-cycle) infinite;
}
.rl-buttons__row[data-gesture="triple"][data-reaction="ap"] .rl-output__icon-ap-wave--2{
  animation:rl-ap-wave-2 var(--rl-cycle) infinite;
}
.rl-buttons__row[data-gesture="triple"][data-reaction="ap"] .rl-output__icon-ap-wave--3{
  animation:rl-ap-wave-3 var(--rl-cycle) infinite;
}

/* ---- STROBE — five+strobe-* (one per mode)
   Six crisp pulses across the post-click window. Each pulse is
   a discrete on/off so "strobe" reads on both motion and static. */

@keyframes rl-strobe-reject-after-five{
  0%, 61%, 95%, 100%                            {fill:var(--rl-led-idle);}
  62%, 65%, 68%, 71%, 74%, 77%, 80%, 83%, 86%   {fill:var(--rl-strobe-reject);}
  66%, 69%, 72%, 75%, 78%, 81%, 84%             {fill:var(--rl-led-idle);}
}
@keyframes rl-strobe-enter-after-five{
  0%, 61%, 95%, 100%                            {fill:var(--rl-led-idle);}
  62%, 65%, 68%, 71%, 74%, 77%, 80%, 83%, 86%   {fill:var(--rl-strobe-enter);}
  66%, 69%, 72%, 75%, 78%, 81%, 84%             {fill:var(--rl-led-idle);}
}
@keyframes rl-strobe-exit-after-five{
  0%, 61%, 95%, 100%                            {fill:var(--rl-led-idle);}
  62%, 65%, 68%, 71%, 74%, 77%, 80%, 83%, 86%   {fill:var(--rl-strobe-exit);}
  66%, 69%, 72%, 75%, 78%, 81%, 84%             {fill:var(--rl-led-idle);}
}
.rl-buttons__row[data-gesture="five"][data-reaction="strobe-red"]   .rl-output__led{animation:rl-strobe-reject-after-five var(--rl-cycle) infinite steps(1,end);}
.rl-buttons__row[data-gesture="five"][data-reaction="strobe-cyan"]  .rl-output__led{animation:rl-strobe-enter-after-five  var(--rl-cycle) infinite steps(1,end);}
.rl-buttons__row[data-gesture="five"][data-reaction="strobe-amber"] .rl-output__led{animation:rl-strobe-exit-after-five   var(--rl-cycle) infinite steps(1,end);}

/* ---- SCENE — single+scene (Mode 2) ---- */

@keyframes rl-led-scene-after-single{
  0%, 35%, 86%, 100%  {fill:var(--rl-led-idle);}
  38%, 82%            {fill:var(--rl-scene);}
}
.rl-buttons__row[data-gesture="single"][data-reaction="scene"] .rl-output__led{
  animation:rl-led-scene-after-single var(--rl-cycle) infinite;
  animation-delay:calc(var(--led-i) * 40ms);
}

/* ---- COLOUR-CYCLE — single+color-cycle (Mode 3)
   Runs on a 16 s schedule (4 × press cycle) so the reader sees
   the full R → G → B → random progression in one visible loop.
   Each 4 s segment lines up with a press: the button press fires
   once per --rl-cycle, the LED advances colour at the same beat. */

@keyframes rl-led-color-cycle{
  0%,  8%   {fill:var(--rl-led-idle);}
  9%,  22%  {fill:var(--rl-cycle-r);}
  23%, 33%  {fill:var(--rl-led-idle);}
  34%, 47%  {fill:var(--rl-cycle-g);}
  48%, 58%  {fill:var(--rl-led-idle);}
  59%, 72%  {fill:var(--rl-cycle-b);}
  73%, 83%  {fill:var(--rl-led-idle);}
  84%, 97%  {fill:var(--rl-cycle-rand);}
  98%, 100% {fill:var(--rl-led-idle);}
}
.rl-buttons__row[data-gesture="single"][data-reaction="color-cycle"] .rl-output__led{
  animation:rl-led-color-cycle calc(var(--rl-cycle) * 4) infinite steps(1,end);
}

/* ---- BRIGHTNESS — hold+bri-local (Mode 3) and hold+bri-fleet (Mode 2)
   Amber pong runs DURING the hold (20–70 %). Fleet variant adds a
   brief broadcast peak right after release (72–78 %); local just
   fades back to idle. */

@keyframes rl-led-bri-local{
  0%, 19%   {fill:var(--rl-bri);opacity:.18;}
  35%       {fill:var(--rl-bri);opacity:1;}
  55%       {fill:var(--rl-bri);opacity:.18;}
  70%       {fill:var(--rl-bri);opacity:.85;}
  71%, 100% {fill:var(--rl-led-idle);opacity:1;}
}
@keyframes rl-led-bri-fleet{
  0%, 19%   {fill:var(--rl-bri);opacity:.18;}
  35%       {fill:var(--rl-bri);opacity:1;}
  55%       {fill:var(--rl-bri);opacity:.18;}
  70%       {fill:var(--rl-bri);opacity:.6;}
  72%, 78%  {fill:var(--rl-bri);opacity:1;}
  79%, 100% {fill:var(--rl-led-idle);opacity:1;}
}
.rl-buttons__row[data-gesture="hold"][data-reaction="bri-local"] .rl-output__led{
  animation:rl-led-bri-local var(--rl-cycle) infinite;
}
.rl-buttons__row[data-gesture="hold"][data-reaction="bri-fleet"] .rl-output__led{
  animation:rl-led-bri-fleet var(--rl-cycle) infinite;
}

/* ============================================================
   Lit-state glow — subtle drop-shadow on actively-coloured LEDs
   and the WiFi icon. Without this, coloured circles read as
   "filled gray dots" rather than "the LED is on". The filter
   only applies WHEN the keyframe has driven the element into a
   non-idle fill, because we attach it to the same combined
   selectors that drive the colour. CPU-cheap: drop-shadow on
   small SVG elements is one of the cheaper filters.
   ============================================================ */

/* AP-row LEDs: cyan halo when fading-in / on */
.rl-buttons__row[data-reaction="ap"] .rl-output__led{
  filter:drop-shadow(0 0 3px rgba(31,230,214,.55));
  /* Filter is permanent BUT only visible while the fill is
     coloured — on grey idle the drop-shadow has nothing to
     glow off. */
}
/* AP icon: stronger cyan halo to suggest a broadcasting signal */
.rl-buttons__row[data-reaction="ap"] .rl-output__icon--ap{
  filter:drop-shadow(0 0 4px rgba(31,230,214,.7));
}

/* Strobes — keep colour-matched glows */
.rl-buttons__row[data-reaction="strobe-red"]   .rl-output__led{filter:drop-shadow(0 0 3px rgba(255,58,58,.6));}
.rl-buttons__row[data-reaction="strobe-cyan"]  .rl-output__led{filter:drop-shadow(0 0 3px rgba(31,230,214,.6));}
.rl-buttons__row[data-reaction="strobe-amber"] .rl-output__led{filter:drop-shadow(0 0 3px rgba(255,178,62,.6));}

/* Scene / colour-cycle / brightness — softer ambient glow */
.rl-buttons__row[data-reaction="scene"]        .rl-output__led{filter:drop-shadow(0 0 3px rgba(57,210,139,.55));}
.rl-buttons__row[data-reaction="color-cycle"]  .rl-output__led{filter:drop-shadow(0 0 3px rgba(255,77,77,.45));}
.rl-buttons__row[data-reaction="bri-local"]    .rl-output__led,
.rl-buttons__row[data-reaction="bri-fleet"]    .rl-output__led{filter:drop-shadow(0 0 3px rgba(255,178,62,.45));}

/* ============================================================
   Reduced motion — kill animations, surface each reaction as a
   static colour so the diagram still teaches its content.
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  .rl-buttons *,
  .rl-buttons *::before,
  .rl-buttons *::after{
    animation:none !important;
    transition:none !important;
  }
  .rl-buttons__row[data-reaction="ap"]           .rl-output__led{fill:color-mix(in srgb,var(--cyan) 32%,transparent);}
  .rl-buttons__row[data-reaction="ap"]           .rl-output__icon--ap   {opacity:1;color:var(--cyan);}
  .rl-buttons__row[data-reaction="ap"]           .rl-output__icon--ap > *{opacity:1;}
  .rl-buttons__row[data-reaction="gated"]        .rl-output__icon--gated{opacity:.85;transform:scale(1);color:var(--muted);}
  .rl-buttons__row[data-reaction="strobe-red"]   .rl-output__led{fill:var(--rl-strobe-reject);}
  .rl-buttons__row[data-reaction="strobe-cyan"]  .rl-output__led{fill:var(--rl-strobe-enter);}
  .rl-buttons__row[data-reaction="strobe-amber"] .rl-output__led{fill:var(--rl-strobe-exit);}
  .rl-buttons__row[data-reaction="scene"]        .rl-output__led{fill:var(--rl-scene);}
  .rl-buttons__row[data-reaction="color-cycle"]  .rl-output__led:nth-child(3){fill:var(--rl-cycle-r);}
  .rl-buttons__row[data-reaction="color-cycle"]  .rl-output__led:nth-child(4){fill:var(--rl-cycle-g);}
  .rl-buttons__row[data-reaction="color-cycle"]  .rl-output__led:nth-child(5){fill:var(--rl-cycle-b);}
  .rl-buttons__row[data-reaction="bri-local"]    .rl-output__led,
  .rl-buttons__row[data-reaction="bri-fleet"]    .rl-output__led{fill:var(--rl-bri);}
}