/* RAVENOR_PROTECTED_SIGNATURE_INTERACTION: original full build / orbit / ascent / terrace. */
html.lenis{scroll-behavior:auto!important}
/* Stage height is the Hero's PACING control, and it is the only one that is scoped to the Hero.
   Lenis's wheelMultiplier would slow the sequence too, but it is global, so buying a better Hero
   with it would make every other route feel sluggish.

   1000svh gave 8100px of travel at a 1440x900 desktop - 14.3px per delivered frame, and a full
   top-to-bottom pass in 5.4s of continuous scrolling. 568 frames of cinematic footage consumed in
   five seconds is the owner's "too fast / too easy to skim", measured.

   Candidates were swept in the browser (qa/hero_stage_candidates.py). The second column is the one
   that decided it: a taller stage does not trade smoothness for pace, it BUYS smoothness, because
   the same gesture now crosses fewer frames and the preloader is no longer outrun.

        svh    px/frame   full pass   peak frame error, fast gesture
       1000       14.3        5.4s        38      <- was here
       1500       22.2        8.4s        19
       1700       25.4        9.6s        13      <- chosen
       2000       30.1       11.4s        10
       2400       36.4       13.8s         7

   1700 is where the sequence becomes something you travel through rather than past, while a
   visitor who only wants the content below still gets there: the cue is a real link to
   #capabilities, and End / PageDown are untouched. 2000+ starts spending the visitor's patience
   rather than their attention. */
.signature-stage{height:1250svh;position:relative;background:var(--bg)}.signature-sticky{position:sticky;top:var(--hh);height:calc(100svh - var(--hh));overflow:hidden;isolation:isolate;background:radial-gradient(ellipse at 48% 48%,#23332a,#0B0D0C 72%)}
#seq{position:absolute;inset:0;width:100%;height:100%;display:block;z-index:1}.signature-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 28%;opacity:1;transition:opacity .4s}.signature-painted .signature-poster{opacity:0}
.signature-vignette{position:absolute;inset:0;z-index:2;pointer-events:none;background:linear-gradient(180deg,#0b0d0c55,transparent 24%,transparent 76%,#0b0d0c),linear-gradient(90deg,#0b0d0c35,transparent 20%,transparent 80%,#0b0d0c55)}
.signature-opening{position:absolute;inset:0;z-index:4;pointer-events:none;display:flex;align-items:center;justify-content:center;flex-direction:column;transform-origin:0 0}/* Production's opening lockup is a contained brand mark, not a band spanning the full width:
   at 155px the wordmark ran edge to edge and the R mark was pushed away from the R it belongs
   against. Smaller, and the mark sits tight to the word so the two read as one lockup. */
.signature-opening .hero-word{font-size:clamp(44px,8.4vw,124px);font-weight:700;line-height:1.14;letter-spacing:.055em;gap:.07em;text-shadow:0 3px 44px #000a;z-index:auto}/* The subtle dark support the production treatment had behind the brand: it lifts the lockup
   off a bright frame without becoming a visible plate, and it leaves with the brand moment. */
.signature-opening::before{content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:min(1220px,88%);height:min(440px,54%);/* no border-radius: production's support is a graphite POOL that fades to nothing, and a
     rounded box around it is exactly the oval container the owner rejected. The falloff is
     what shapes it. */background:radial-gradient(46% 140% at 50% 50%,rgba(6,8,7,.72),rgba(6,8,7,.34) 42%,rgba(6,8,7,.11) 60%,rgba(6,8,7,0) 78%);z-index:-1;pointer-events:none;transition:opacity .55s var(--ease)}body.word-docked .signature-opening::before{opacity:0}.signature-opening .signature-sub{font-size:clamp(10px,1.3vw,15px);letter-spacing:.18em;color:var(--sage-bright);margin-top:22px;text-align:center;padding-inline:22px;text-shadow:0 2px 12px #000}
.signature-beat{position:absolute;top:50%;left:clamp(24px,5vw,72px);right:auto;z-index:3;max-width:430px;opacity:0;pointer-events:none;transform:translateY(-50%);transition:opacity .2s;text-shadow:0 3px 26px #000}.signature-beat[data-dir=right]{left:auto;right:clamp(60px,8vw,130px);text-align:right}.signature-beat h2{font:500 clamp(26px,3.4vw,49px)/1.15 var(--font-body);letter-spacing:-.035em;color:var(--sage-bright)}.signature-beat p{font-size:14px;line-height:1.7;margin-top:16px;color:var(--text-hi)}
/* ------------------------------------------------------------------ THE DESCENT
   The owner's note was that this still read as a button, and it did: a 12px radius, a border and
   a padded box that filled in with a bordered, blurred plate on hover - 182x74 of chrome to carry
   eleven characters and a chevron. A visitor who reads "button" reaches for the mouse, and the
   behaviour this cue wants is the opposite: keep scrolling. The box is gone. What remains is the
   composition that was always doing the work - a label, a measure drawn down from it, and a mark
   at the foot - which is a drafting dimension line, and therefore Ravenor's own vocabulary rather
   than a generic scroll pill. The anchor still exists for the keyboard, and jumping past a
   seven-screen hero is a real convenience; it simply no longer advertises itself as the main way
   through.
   Hover and focus now brighten the PARTS instead of drawing a container around them. */
.signature-cue{position:absolute;z-index:4;bottom:38px;left:50%;transform:translateX(-50%);
  display:flex;flex-direction:column;align-items:center;gap:11px;
  padding:6px 14px;border:0;background:none;border-radius:0;
  color:var(--text-rev);font-size:10px;letter-spacing:.24em;text-transform:uppercase;
  line-height:1;min-height:44px;white-space:nowrap;text-decoration:none;
  transition:color var(--t-ctl,.28s) var(--ease)}
/* The two 60px sage rules that used to flank the label were the loudest thing in the hero's
   lower third - 160px of bright line to introduce eleven characters of 11px type. The control
   is now a VERTICAL descent, which is also what it is actually asking the visitor to do: label,
   a short measure drawn down from it, then the mark. */
.signature-cue::before,.signature-cue::after{content:none}
.cue-label{display:block}
/* 15px read as a tick; 30px reads as a measure, and the difference is most of why the old cue
   needed a box to look like anything at all. */
.cue-label::after{content:'';display:block;width:1px;height:30px;margin:11px auto 0;
  background:linear-gradient(180deg,rgba(171,201,176,.10),var(--sage));
  transform-origin:top;animation:rv-cue-line 3.6s var(--ease) infinite;
  transition:background var(--t-ctl,.28s) var(--ease)}
.cue-mark{width:9px;height:9px;flex:none;
  border-right:1.5px solid var(--sage);border-bottom:1.5px solid var(--sage);
  transform:rotate(45deg);animation:rv-cue-breathe 3.6s var(--ease) infinite;
  transition:border-color var(--t-ctl,.28s) var(--ease),opacity var(--t-ctl,.28s) var(--ease)}
/* light arrives and recedes; it never flashes */
@keyframes rv-cue-line{0%,100%{opacity:.42;transform:scaleY(.72)}46%{opacity:1;transform:scaleY(1)}}
@keyframes rv-cue-breathe{0%,100%{opacity:.6;transform:rotate(45deg) translate(0,-1.5px)}
  46%{opacity:1;transform:rotate(45deg) translate(0,1.5px)}}
@media(hover:hover){
  /* No plate, no border, no blur - the light simply comes up in the three parts that are already
     there, and the mark steps down by the distance it is inviting. */
  .signature-cue:hover{color:var(--text-hi)}
  .signature-cue:hover .cue-label::after{background:linear-gradient(180deg,rgba(171,201,176,.34),var(--sage-br,var(--sage)))}
  /* Hover must keep the mark's own geometry. This rule outranks the refined .cue-mark block
     further down (0,2,1 against 0,1,0), so writing a bare rotate(45deg) here silently snapped the
     shallow drawn mark back to a 90-degree UI chevron for exactly as long as the pointer was on
     it - the shape changing under the cursor, which is the kind of detail the owner notices.
     The scale is carried through, and the invitation is a 2px step in the direction the cue
     points, never against it. */
  .signature-cue:hover .cue-mark{border-color:var(--sage-br,var(--sage));opacity:1;
    transform:rotate(45deg) scale(1,.74) translate(2px,2px)}
}
.signature-cue:focus-visible{outline:2px solid var(--sage);outline-offset:6px;border-radius:6px}
@media(prefers-reduced-motion:reduce){
  .cue-label::after,.cue-mark{animation:none}
  .cue-label::after{opacity:.85;transform:none}
  .cue-mark{opacity:.9;transform:rotate(45deg)}
}
.signature-progress{position:absolute;right:30px;top:50%;transform:translateY(-50%);display:flex;align-items:flex-end;gap:16px;flex-direction:column;z-index:4;color:var(--muted);font:10px var(--font-mono)}/* Horizontal, above the track - the way production presents it. Rotated 10px Armenian is
   legible but reads as broken at 100% zoom, and there is no reason to rotate a label that
   has room to sit upright. */
.signature-progress .stage-label{writing-mode:horizontal-tb;letter-spacing:.14em;color:var(--sage-bright);white-space:nowrap;text-transform:uppercase}.signature-track{height:150px;width:2px;background:#abc9b033}.signature-track i{display:block;width:100%;height:100%;background:var(--sage);transform:scaleY(var(--hero-progress,0));transform-origin:top}.signature-end{position:absolute;inset:0;background:var(--surface-1);pointer-events:none;z-index:5;opacity:0;clip-path:inset(100% 0 0)}
.signature-static-copy{display:none}.signature-static .signature-stage{height:auto}.signature-static .signature-sticky{position:relative;top:0;min-height:700px;height:calc(100svh - var(--hh))}.signature-static .signature-cue,.signature-static .signature-progress{display:none}.signature-static .signature-static-copy{display:block;position:absolute;z-index:5;bottom:60px;left:clamp(24px,5vw,72px);max-width:360px}.signature-static-copy p{font-size:15px;line-height:1.7;margin-bottom:22px}.signature-static .signature-opening{height:60%}.signature-static .rv-brand-word{opacity:1!important}
/* Mobile was the worse of the two, not the milder one. 750svh gave 5486px at 390x844 - 9.7px per
   frame and a full pass in 3.7 seconds, against the desktop's 5.4, and a phone has native momentum
   (smoothTouch is off, deliberately) so one flick throws further than any wheel gesture can. It
   also starved hardest: peak frame error 64 under a fast gesture, where desktop managed 38.
   1400svh gives 19.3px/frame, a 7.3s pass and a peak error of 13 - the same smoothness the chosen
   desktop value reaches, which is the parity worth having. */
@media(max-width:820px){.signature-stage{height:1250svh}.signature-opening .hero-word{font-size:10.5vw}.signature-opening{justify-content:flex-start;padding-top:26%}.signature-opening .signature-sub{font-size:10px;letter-spacing:.075em;max-width:340px;line-height:1.8;margin-top:16px}.signature-poster{object-fit:contain;object-position:48% 48%;mix-blend-mode:lighten}.signature-beat,.signature-beat[data-dir=right]{top:auto;bottom:105px;left:24px;right:24px;max-width:340px;text-align:left;transform:none!important}.signature-beat h2{font-size:29px;max-width:320px}.signature-progress{top:auto;bottom:30px;left:24px;right:24px;transform:none;flex-direction:row;justify-content:center;gap:18px}.signature-progress .stage-label{writing-mode:horizontal-tb;font-size:9px;letter-spacing:.06em}.signature-track{width:85px;height:2px}.signature-track i{transform:scaleX(var(--hero-progress,0));transform-origin:left}.signature-cue{bottom:90px;font-size:10px;gap:16px}.signature-cue::before,.signature-cue::after{width:35px}.signature-static .signature-sticky{min-height:650px}.signature-static .signature-static-copy{bottom:70px;right:24px}.signature-vignette{background:linear-gradient(180deg,#0b0d0c77,transparent 24%,transparent 70%,#0b0d0c)}}
@media(prefers-reduced-motion:reduce){.signature-poster,.signature-beat,.signature-cue{transition:none}}

@media(max-width:820px){.signature-progress{bottom:84px}.signature-cue{bottom:122px}.signature-beat,.signature-beat[data-dir=right]{bottom:144px}}
@media(max-width:820px){#seq{mix-blend-mode:lighten}.signature-sticky{background:radial-gradient(ellipse at 48% 42%,#1b2721,#0b0d0c 80%)}.signature-poster{width:192.308%;height:auto;inset:42% auto auto -43.269%;transform:translateY(-42%);mask-image:linear-gradient(to bottom,transparent,#000 7%,#000 73%,transparent)}}
.signature-static #seq,.signature-static .signature-video,.signature-static .signature-beat,.signature-static .signature-end{display:none}.signature-static .signature-poster{opacity:1}

/* Hero OFF is a designed product mode, so the still gets its own composition instead of
   inheriting the animated hero's. Two things were wrong when the mode was first rendered.
   Desktop: the opening block is centred in the top 60%, which drops the subtitle straight onto
   the kiosk's white fascia - near-white type on white. Mobile: the poster keeps the framing
   built for the CANVAS (width:192.308%, inset left -43.269%), which is aimed at where the
   subject sits DURING the sequence; on a single completed frame it pushes the kiosk off the
   left edge and leaves the rest of the screen empty. Both are scoped to .signature-static so
   the protected signature interaction is untouched. */
.signature-static .signature-opening{height:46%}
.signature-static .signature-opening .signature-sub{text-shadow:0 2px 14px #000c,0 0 4px #0009}
@media(max-width:820px){
 .signature-static .signature-poster{width:175%;height:auto;inset:auto auto 24% 2%;transform:none;object-fit:contain;mask-image:linear-gradient(to bottom,transparent,#000 9%,#000 80%,transparent)}
 .signature-static .signature-opening{height:auto;inset:9% 0 auto;padding-top:0}
 .signature-static .signature-static-copy{bottom:48px;left:24px;right:24px;max-width:none}
}

/* ==============================================================================================
   PRODUCTION HERO RESTORATION
   Recovered from local git authority, not recreated by eye: every value below is taken from
   `git show HEAD:site/index.html`, which is the .stage / .beat.hero implementation ravenor.am
   serves. Production visuals; the local 507-frame scroll engine is untouched.
   ============================================================================================== */

/* Production's hero tracking token. It does not exist in the local sheets, so `letter-spacing:
   var(--tr-hero)` was invalid at computed-value time and the property fell all the way back to
   `normal` - discarding the lower-specificity .05em as well, which is why the lockup measured
   81px narrower than production at an identical font size. */
:root{--tr-hero:.14em}

/* The stage's own ground: a graphite band behind the header, pure black below it. THIS is the
   production "black treatment" - it is not a letterbox, and the frame is still drawn edge to edge. */
.signature-stage{background:linear-gradient(180deg,#0e1210 0,#0b0e0c var(--hh),#000 var(--hh))}

/* `main` already pads itself by the header height, so the stage began at --hh and the canvas
   inset put the frame at 2x the header - production's stage is not inside a padded main. Pull
   the stage back to the top of the document so the sticky pins at 0 exactly as production
   does; the sections after it are unaffected because the stage's own height does not change.
   Static-hero mode keeps the padding, since there is no sticky pin to align there. */
body:not(.signature-static) .signature-stage{margin-top:calc(-1 * var(--hh))}

/* Production pins the sticky at the top of the viewport and insets the VIDEO instead of the
   container. That is what lets the opening lockup travel up into the header band during the
   hand-off: with the sticky itself inset, its own overflow:hidden clipped the mark before it
   could reach the header. The hero still never runs under the bar, because the canvas - not the
   container - starts at the header's lower edge. */
.signature-sticky{top:0;height:100svh}
#seq{top:var(--hh);height:calc(100% - var(--hh))}
.signature-poster{top:var(--hh);height:calc(100% - var(--hh));object-position:center 28%}

.signature-vignette{inset:var(--hh) 0 0;background:
  radial-gradient(150% 46% at 50% -12%,rgba(226,238,228,.085),rgba(226,238,228,.03) 46%,transparent 72%),
  radial-gradient(80% 30% at 50% -4%,rgba(171,201,176,.06),transparent 70%),
  radial-gradient(120% 120% at 50% 45%,transparent 55%,rgba(0,0,0,.55) 100%)}

/* opening, captions and rail sit in the video area, exactly as production's .beats does */
.signature-opening{inset:var(--hh) 0 0}
.signature-beat{top:calc(50% + var(--hh)/2)}
.signature-progress{top:calc(50% + var(--hh)/2)}

/* The opening lockup at production's own values: weight 900, 6.4vw capped at 82px, .3em between
   the mark and the word, and the two shadows that lift it off the lit steel. */
/* The hero lockup was the last brand instance outside the system, at .82em against a .72em cap -
   a 1.14 ratio, next to 2.6 in the header, the footer, the Assistant and Admin. It is also the
   most prominent brand moment on the site, so it was the most visible exception.
   There is a second reason beyond consistency. The hero word DOCKS into the header, and home.js
   performs that as a uniform scale (b.width / a.width). Two lockups with different mark-to-cap
   ratios cannot be mapped onto each other by a scale, so the mark had to change proportion
   relative to the word during the travel. Same ratio at both ends makes the dock a pure scale and
   the brand hold its shape all the way into the bar. */
.signature-opening .hero-word{font-family:var(--font-brand);font-weight:900;
  font-size:clamp(32px,6.4vw,82px);letter-spacing:var(--tr-hero);line-height:1.12;
  gap:var(--brand-mark-gap,.187em);
  text-shadow:0 2px 22px rgba(0,0,0,.75),0 0 60px rgba(0,0,0,.5)}
.signature-opening .hero-word img{height:var(--brand-mark-h,1.872em);width:auto;
  aspect-ratio:var(--brand-mark-aspect,552 / 763);flex:none;object-fit:contain;vertical-align:0;
  filter:drop-shadow(0 0 30px rgba(171,201,176,.62)) drop-shadow(0 3px 14px rgba(0,0,0,.8))}
.signature-opening .hero-word span{position:static}
/* the graphite pool behind the lockup - a pool that fades to nothing, never a plate */
.signature-opening::before{width:min(1180px,94%);height:min(600px,78%);
  background:radial-gradient(46% 140% at 50% 50%,rgba(6,8,7,.72),rgba(6,8,7,.34) 42%,
    rgba(6,8,7,.11) 60%,rgba(6,8,7,0) 78%)}
.signature-opening .signature-sub{letter-spacing:var(--tr-eyebrow);margin-top:18px;
  font-size:clamp(11px,1.3vw,14px);font-weight:500;text-shadow:0 1px 12px rgba(0,0,0,.85)}

@media(max-width:820px){
  .signature-opening{inset:var(--hh) 0 0;justify-content:center;padding-top:0}
  .signature-opening .hero-word{font-size:clamp(31px,10.4vw,50px);letter-spacing:var(--tr-hero)}
  .signature-opening .signature-sub{font-size:10.5px;margin-top:13px;max-width:340px}
  .signature-opening::before{width:96%;height:58%}
}

/* Production shows a scrim over the lower part of the mobile hero (`.hero-scrim`, display:block
   below 820px) so the defocused ground falls away to black under the frame instead of staying a
   flat grey field. Reproduced here as a gradient layer on the vignette rather than new DOM, so
   the hero markup is untouched. */
@media(max-width:820px){
  .signature-vignette{background:
    linear-gradient(180deg,rgba(8,10,9,0) 46%,rgba(8,10,9,.52) 66%,rgba(8,10,9,.86) 84%,rgba(8,10,9,.96) 100%),
    radial-gradient(150% 40% at 50% -10%,rgba(226,238,228,.07),transparent 66%),
    radial-gradient(120% 120% at 50% 45%,transparent 52%,rgba(0,0,0,.6) 100%)}
}

/* The assembly rail sat directly under the floating quote tab, which is pinned to the middle of
   the right edge - the percentage read as if it belonged to the trigger. Move the rail inboard so
   the two never occupy the same column. */
.signature-progress{right:clamp(62px,6.5vw,104px)}
@media(max-width:820px){.signature-progress{right:24px;left:24px}}

/* ------------------------------------------------- the readout must not eat the control
   `.signature-progress` is a READOUT - a stage label, a track and a percentage. It has no
   interactive children, but it is a positioned box at z-index 4, and below 820px it stretches
   `left:24px;right:24px` across the hero. That box lands on top of Scroll-to-explore, so on a
   phone every tap on the cue hit the readout instead and the control simply did not work.
   Verified with a real click through the cue on 390px before and after: intercepted by
   .signature-progress, then landing on #capabilities.

   Scroll-to-explore is protected functionality, and a visitor who does not want to watch the
   whole sequence has no other way past it, so this is a functional fix, not a cosmetic one. */
.signature-progress{pointer-events:none}

/* The poster is the first paint, before the canvas has decoded frame 1. It has to be framed the
   way the canvas frames - otherwise the hero visibly jumps a step closer the instant the
   sequence takes over, which is the one moment every visitor sees. `cover` plus a scale about
   the centre reproduces home.js's DZOOM exactly; the small translate is its DBIAS, the headroom
   that keeps the top steel clear of the header. Scoped away from the static hero, which is a
   designed still with its own composition. */
/* Desktop poster placement lives in home.js (placePoster/box), not here: the fit is piecewise
   - contain with air, floored so a tall viewport does not letterbox the build into a postcard -
   and no object-fit plus transform reproduces that at every aspect ratio. A stylesheet that
   approximates it is a stylesheet that makes the hero jump the moment the canvas takes over. */
/* Match the accepted canvas crop (MCROP=.6875, MFOCX=.485, MBIAS=.20).
   The global image max-width otherwise clamps this deliberately oversized still to the
   viewport, shifting the architecture before the first frame or when motion cannot load.
   The canvas starts below --hh; account for that inset in the poster's vertical position.
   Static Hero composition is independent and retains its existing rules. */
body:not(.signature-static) .signature-poster{max-width:none}
@media(max-width:820px){
 body:not(.signature-static) .signature-poster{width:145.454545%;height:auto;
   inset:calc(20% + var(--hh)*.8) auto auto -20.545455%;transform:translateY(-20%);object-fit:contain}
}

/* ------------------------------------------------------------------ THE ARRIVAL
   The hero lockup travels up and docks into the header; at the moment it lands, the R mark
   lights and settles. Ravenor's own sage, and only the mark - a wordmark that turns green stops
   being a detail and becomes an effect, which is the opposite of what was asked for.

   The base state is the drop-shadow the header mark already carries, so the animation starts and
   ends exactly where the mark lives and there is no step at either end. The peak adds a second,
   tighter shadow in --sage-br and lifts the mark itself a little, so it reads as the mark
   illuminating rather than as a halo being switched on behind it. One pass, ~0.9s, ease-out
   arrival: light comes up fast, leaves slowly.

   home.js fires this on the TRANSITION into the docked state and collapses repeats inside 1.5s,
   so a fast flick across the hand-off point is one arrival rather than a blink. */
@keyframes rv-r-arrive{
  0%  {filter:drop-shadow(0 0 14px rgba(171,201,176,.35)) brightness(1)}
  32% {filter:drop-shadow(0 0 30px rgba(194,220,198,.92)) drop-shadow(0 0 8px rgba(194,220,198,.60))
       brightness(1.20)}
  100%{filter:drop-shadow(0 0 14px rgba(171,201,176,.35)) brightness(1)}
}
.rv-hdr .brand.r-arrive img{animation:rv-r-arrive 1.05s cubic-bezier(.16,.84,.32,1) 1}
@media(prefers-reduced-motion:reduce){.rv-hdr .brand.r-arrive img{animation:none}}

/* ------------------------------------------------------------------ THE DESCENT, refined
   The composition was already right - a label, a measure drawn down from it, a mark at the foot,
   which is a drafting dimension line and therefore Ravenor's own vocabulary rather than a scroll
   pill. What it did not do was say DOWN. The line breathed in place, which is a pulse, not a
   direction, and a first-time visitor has to infer the rest.

   So the line now carries a travelling light: a soft segment that enters at the top, runs the
   length of the measure and leaves at the foot, and the mark brightens and steps down as it
   arrives. Motion with a direction in it is the instruction, which is why the words do not have
   to be one - and why there is still no box, no border and no plate to reach for with a mouse.
   It rides one background-position on a 1px column, so it costs nothing to animate. */
/* ---- the loop, rebuilt so a reset cannot happen -------------------------------------------
   The previous version had both faults the owner described, and they are visible in its own
   keyframes rather than a matter of taste:

     rv-cue-travel  ran 10% -> 58%, then HELD at 155% from 58% to 100%. Two fifths of every
                    cycle - 1.4s of a 3.4s loop - was dead air. That is the "stops awkwardly".
     rv-cue-arrive  put the mark at translate(1.5px,1.5px) at 58% and back at translate(0,-1px)
                    by 84%. The mark did not fade and return, it TRAVELLED BACK UP the stem over
                    0.9s, against the direction the cue exists to indicate. That is the
                    "jumps back upward", and it was the one thing on screen moving the wrong way.

   Placing those keyframes more carefully would only hide the fault. Both are now impossible by
   construction instead:

   1. The light is one tile of a repeating column, shifted by EXACTLY one tile height over the
      cycle, with LINEAR timing. The image at 100% is therefore pixel-identical to the image at
      0% - not approximately, identically - so there is no reset to see at any duration, on any
      refresh rate, and no keyframe can drift out of sync with another. Softness comes from the
      gradient's own falloff (spatial), not from an easing curve (temporal), which is what lets
      the timing stay linear without reading as mechanical.
   2. The mark no longer moves at all. It answers the light in brightness only. An opacity curve
      has no direction, so it cannot travel backwards; 0% and 100% hold the same value, so it
      cannot pop.

   Geometry: stem H=46px, tile T=105px, one shift of exactly T per 3s. The lit band is 13% of the
   tile - about 14px against the stem's 46 - so what crosses is a SEGMENT with dark stem above and
   below it, which is what makes the motion read as a descent rather than as a glow.

   The two numbers that matter both fall out of T, so they are derived here rather than guessed.
   At animation progress u the band's peak sits at T*((u + pk) mod 1), with pk=0.81 its position
   in the tile:
       travel share  = H/T          = 0.438  ->  1.31s crossing, 1.69s dark
       peak at foot  = H/T - pk     = 0.628  ->  where the mark must peak
   The mark's arrival keyframe is therefore 63%, and if T or pk ever change, that number has to be
   recomputed with them - a light that lands before or after its own marker is the "mismatch
   between line and marker" this rebuild exists to avoid. At T=130 the rhythm was 1.06s of travel
   against 1.94s of rest, which read as a cue that had stopped rather than one that was breathing.

   That proportion is the whole effect, and getting it wrong is not subtle. The first version of
   this rule used a 42px band on the 46px stem; it was seamless, it never ran backwards, and it
   was still wrong, because a band nearly as tall as the stem lights the whole stem at once and
   the thing you see is the line brightening and dimming in place. qa/hero_cue_loop_probe.py
   caught it by measuring the brightest row per phase and finding it never moved - the same
   "breathes in place, which is a pulse, not a direction" fault this cue was rebuilt to remove.

   One pass per cycle: the segment enters the top at u=0.19, its peak reaches the foot at u=0.628,
   and the stem is dark from there round to 0.19 again. One travel, one arrival, one breath. */
.cue-label::after{height:46px;margin:12px auto 0;
  background:
    linear-gradient(180deg,
      rgba(194,220,198,0) 0,       rgba(194,220,198,0) 74%,
      rgba(194,220,198,.62) 77.5%, rgba(194,220,198,.95) 81%,
      rgba(194,220,198,.55) 84%,   rgba(194,220,198,0) 87%,
      rgba(194,220,198,0) 100%) 0 0 / 1px 105px repeat-y,
    linear-gradient(180deg,rgba(171,201,176,.10),rgba(171,201,176,.42));
  animation:rv-cue-travel 3s linear infinite;
  transform:none}
@keyframes rv-cue-travel{
  from{background-position:0 0,0 0}
  to  {background-position:0 105px,0 0}
}
/* The mark, refined. A 90-degree corner is the default chevron and reads as a UI arrow; scaling
   the rotated square on one axis opens the included angle to ~107 degrees, which is the shallower,
   drawn mark an architectural drawing uses - and 1px strokes rather than 1.2px keep it a notch
   on the measure instead of a button. Brightness only, on the same 3s clock as the light, peaking
   at 63% where the light reaches the foot. The soft glow rides the same curve so it cannot
   flicker independently of the mark it belongs to. */
.cue-mark{width:11px;height:11px;border-right-width:1px;border-bottom-width:1px;
  transform:rotate(45deg) scale(1,.74);
  animation:rv-cue-arrive 3s cubic-bezier(.4,0,.3,1) infinite}
@keyframes rv-cue-arrive{
  0%   {opacity:.34;filter:drop-shadow(0 0 0 rgba(194,220,198,0))}
  42%  {opacity:.46}
  63%  {opacity:1;filter:drop-shadow(0 1px 4px rgba(194,220,198,.42))}
  82%  {opacity:.50}
  100% {opacity:.34;filter:drop-shadow(0 0 0 rgba(194,220,198,0))}
}
@media(prefers-reduced-motion:reduce){
  .cue-label::after{animation:none;
    background:linear-gradient(180deg,rgba(171,201,176,.12),rgba(171,201,176,.55));
    opacity:1}
  /* The static fallback keeps the refined geometry - only the travel is dropped, so the cue
     still reads as the same mark rather than as a different control. */
  .cue-mark{animation:none;opacity:.9;filter:none;transform:rotate(45deg) scale(1,.74)}
}
