/* ==========================================================================
   EVENTGO MOTION IDENTITY
   --------------------------------------------------------------------------
   Three systems, one vocabulary:

     A. Event Trail  — desktop pointer: four objects trailing with their own
                       follow behaviour, assembling into a pass on large
                       surfaces
     B. Touch Trail  — the same four objects, briefly, around a genuine tap
     C. Route Pass   — page transitions built from the same four objects

   All three draw from the ONE sprite in the markup, so they cannot drift
   apart. Each object carries a single brand colour: four small shapes each
   holding a three-stop gradient reads as noise at 20px.

   Interaction reference for the trailing principle only: cravburgers.shop.
   No asset, shape, colour, physics constant or line of code is theirs — their
   objects are burger ingredients, these are the things an events platform is
   actually made of.

   Loaded second, right after 00-base, because the Route Pass backdrop has to
   paint before any page content can flash underneath it.
   ========================================================================== */

.eg-m-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* The images carry their own colour, lighting and shadow, so there is
   nothing to paint from CSS any more. The four custom properties that used
   to tint the SVG faces are gone with the symbols. */

/* ==========================================================================
   A · EVENT TRAIL
   ========================================================================== */

.eg-m-trail {
  position: fixed; inset: 0; z-index: var(--eg-z-cursor);
  pointer-events: none; display: none; opacity: 0;
}
html.eg-m-on .eg-m-trail { display: block; }

/* ==========================================================================
   THE NATIVE CURSOR IS NEVER HIDDEN. THIS IS THE RULE, NOT AN OVERSIGHT.
   --------------------------------------------------------------------------
   `cursor: none` used to sit here, on html, body, a and button, with a
   carve-out handing inputs their caret back. It is gone, and it must not
   come back.

   The trail is DECORATION BEHIND THE POINTER, not a replacement for it. The
   native cursor is the interaction point, and the browser already says more
   with it than a drawn object can: an arrow over text, a hand over a link,
   an I-beam in a field, a resize arrow on an edge, a grab hand mid-drag, a
   wait cursor when the page is busy, and whatever the reader's own operating
   system accessibility settings have made of all of those — size, colour,
   contrast, a system-wide pointer theme. Hiding it discards every one of
   them and replaces them with four pictures that know nothing about state.

   The carve-out list was the tell. `cursor: none` needed an exception for
   inputs, then for [draggable], and every new interactive surface would have
   needed another; a rule that has to enumerate where it must not apply is
   the wrong rule. There is nothing left to enumerate now.

   The ticket instead hangs off the pointer's LEADING NOTCH — see
   EG.M.C.ANCHOR in eg-motion.js — so the arrow reads as pulling it. The
   cursor and the trail are both visible, and the trail is the one that
   gives way.
   ========================================================================== */

/* ---- The spline ---------------------------------------------------------
   The path is a major visual element, not a hint. The first version ran at
   0.55 opacity and was effectively invisible in a screenshot; the reference
   makes the line as present as the objects on it.
   -------------------------------------------------------------------- */

.eg-m-trail__svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
}
.eg-m-trail__line {
  fill: none; stroke: url(#egmTrailGrad);
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}

/* ---- The objects --------------------------------------------------------
   Transparent WebP images, not SVG. Three SVG treatments were tried across
   two passes — flat glyphs, badged glyphs, semi-dimensional glyphs — and
   every one still read as an icon beside the reference's physical objects.
   The assets carry their own gradients, lit edge and cast shadow, so there
   is nothing to paint from here.

   Built by tools/make-motion-assets.mjs; briefed in
   docs/MOTION-ASSET-BRIEF.md.
   -------------------------------------------------------------------- */

.eg-m-obj {
  position: absolute; top: 0; left: 0;
  display: block; will-change: transform;
}
.eg-m-obj img {
  width: 100%; height: 100%; display: block;
  object-fit: contain; pointer-events: none; user-select: none;
}

/* THERE ARE NO SIZES HERE, AND THAT IS DELIBERATE.

   This file used to set a SQUARE width/height per object — 66/58/54/48 for
   the cursor. Five of the six drawings are not square, so `object-fit:
   contain` letterboxed the art inside that square and the drawing's own
   transparent padding ate the rest: the "66px" ticket painted 55x36.

   Size is now one thing in one place: EG.M.box() in assets/v3/js/eg-motion.js
   turns a configured VISUAL size into a render box with the image's own
   aspect ratio, and EG.M.size() writes it. `contain` is kept only as a
   guarantee that a future asset whose aspect drifts from EG.M.DIM is letter-
   boxed rather than stretched.

   Do not reintroduce a width here. A second authority on size is how the
   cursor came to be a different scale from the loader. */

/* ---- The assembly ------------------------------------------------------- */

/* Over a large opted-in surface the curve collapses, the four objects
   converge and this takes their place: one compact pass carrying the verb.
   Over small targets it never appears — a disc over a button hides the
   button's own label, which is the failure the four-mode design exists to
   avoid. */
.eg-m-trail__pass {
  position: absolute; top: 0; left: 0;
  display: grid; place-items: center;
  width: 104px; height: 58px;
  opacity: 0; will-change: transform;
  border-radius: 10px;
  background: color-mix(in srgb, var(--c-ink) 88%, transparent);
  -webkit-mask:
    radial-gradient(circle 8px at 0 50%, transparent 98%, #000 100%),
    radial-gradient(circle 8px at 100% 50%, transparent 98%, #000 100%),
    linear-gradient(#000 0 0);
  -webkit-mask-composite: source-in;
  mask:
    radial-gradient(circle 8px at 0 50%, transparent 98%, #000 100%),
    radial-gradient(circle 8px at 100% 50%, transparent 98%, #000 100%),
    linear-gradient(#000 0 0);
  mask-composite: intersect;
}
.eg-m-trail__pass::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  padding: 1.5px;
  background: linear-gradient(96deg, var(--eg-violet), var(--eg-blue), var(--eg-teal));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
/* The perforation, so the assembled shape is a ticket and not a rounded box. */
.eg-m-trail__pass::after {
  content: ""; position: absolute; top: 9px; bottom: 9px; left: 30px; width: 1px;
  background: repeating-linear-gradient(
    to bottom, color-mix(in srgb, var(--c-text) 40%, transparent) 0 3px, transparent 3px 6px);
}
.eg-m-trail__label {
  position: relative; padding-left: 14px;
  font-size: .625rem; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase; color: var(--c-text); white-space: nowrap;
}

/* ==========================================================================
   B · TOUCH TRAIL
   ========================================================================== */

/* ABOVE THE ROUTE PASS (1000), DELIBERATELY.

   A tap on a link fires the Touch Trail and the Route Pass exit at the same
   moment. At z-index 880 the rising cover painted straight over the burst
   about 160ms in, so the reader saw half a gesture and then a wipe — two
   effects, one interrupting the other. On top, the pieces converge into the
   control while the cover rises behind them, which is the one movement the
   brief asks for: tap, EventGo response, destination.

   It is pointer-events:none and aria-hidden, so being the topmost layer
   costs nothing and blocks nothing. */
.eg-m-touch { position: fixed; inset: 0; z-index: var(--eg-z-touch); pointer-events: none; }
.eg-m-touch .eg-m-obj { opacity: 0; }
.eg-m-touch__svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
}
.eg-m-touch__line {
  fill: none; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ==========================================================================
   C · ROUTE PASS
   --------------------------------------------------------------------------
   The backdrop is plain CSS and paints the instant the class is on <html>,
   before any page content can appear underneath it. The STAGE is empty in the
   markup — eg-routepass.js builds each route's own visuals into it, because
   shipping eleven variants' markup to every page would be dead weight on ten
   of them.
   ========================================================================== */

.eg-m-rp { display: none; }
html.eg-rp .eg-m-rp { display: block; }
html.eg-rp { overflow: hidden; }
html.eg-rp body { overflow: hidden; }

.eg-m-rp {
  position: fixed; inset: 0; z-index: var(--eg-z-route);
  pointer-events: auto;
}
/* `inset: 0` on a fixed element is the layout viewport, which on a mobile
   browser is not always what the reader can see: the moment the address bar
   collapses, the visible area grows and a backdrop sized to the old box can
   leave a strip of live page along the bottom edge mid-entrance.

   `dvh` follows the visible area, so the backdrop tracks the toolbar. It is
   behind @supports because an engine without dynamic units must keep
   `inset: 0` rather than lose its height entirely, and the two cannot be
   expressed as a fallback pair — `bottom: 0` and `height` fight. */
@supports (height: 100dvh) {
  .eg-m-rp { bottom: auto; height: 100dvh; min-height: 100%; }
}

/* SAFE AREAS. The viewport meta deliberately does NOT carry
   `viewport-fit=cover`, so iOS insets the whole page inside the safe area
   and nothing can land under a notch, a Dynamic Island, a cutout or the
   home indicator. These paddings are therefore zero today, and they are
   here so that turning cover mode on later is a one-line change to the meta
   rather than a hunt for everything it breaks. `env()` resolves to 0 in
   every browser that does not implement it, so this costs nothing. */
.eg-m-rp__bar { bottom: env(safe-area-inset-bottom, 0px); }
.eg-m-rp__bg {
  position: absolute; inset: 0;
  background: var(--c-ink);
  will-change: transform, opacity;
}
/* A faint brand wash so the ground is never flat black. */
.eg-m-rp__bg::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(46% 42% at 28% 26%,
      color-mix(in srgb, var(--eg-violet) 24%, transparent), transparent 70%),
    radial-gradient(40% 38% at 74% 78%,
      color-mix(in srgb, var(--c-teal) 17%, transparent), transparent 70%);
}
.eg-m-rp__stage {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: var(--c-gut);
}
/* ---- The message --------------------------------------------------------
   This used to be a 10px letter-spaced eyebrow pinned to the bottom of the
   screen. Nobody read it, which is most of why the loaders "communicated too
   little": the picture was doing all the work and the sentence was furniture.

   It is now the largest thing on the loader, centred, and each route holds
   it long enough to actually be read — roughly 600ms of settled presence,
   which is about what the reference gives each of its stages.
   ---------------------------------------------------------------------- */

.eg-m-rp__say {
  position: absolute; left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  display: grid; justify-items: center; gap: 1rem;
  /* top right bottom left — the insets belong on the sides they name. */
  padding: 0 max(var(--c-gut), env(safe-area-inset-right))
           0 max(var(--c-gut), env(safe-area-inset-left));
  text-align: center;
  pointer-events: none;
}
/* Every beat OVERLAPS in one cell. Appending each beat as a sibling in flow
   made the block grow with each act, which pushed the supporting line down
   the screen and straight across the curve. */
.eg-m-rp__words, .eg-m-rp__subs { display: grid; justify-items: center; }
.eg-m-rp__words > *, .eg-m-rp__subs > * { grid-area: 1 / 1; }
.eg-m-rp__words { min-height: 1em; }
.eg-m-rp__subs { min-height: 1.2em; }

/* RESERVE THE FINAL LAYOUT BEFORE THE WORDS ARRIVE.
   On a phone the display word wraps to two lines and the caption to two, and
   because each beat only reveals its own line the block changed height as
   the story played — the caption stepped down the screen between acts. The
   tallest beat's box is reserved up front so every act is composed against
   the same centre. */
@media (max-width: 560px) {
  .eg-m-rp__words { min-height: 2.05em; }
  .eg-m-rp__subs  { min-height: 2.6em; }
  .eg-m-rp__say   { gap: .75rem; }
}
.eg-m-rp__word {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(2.4rem, 8vw, 5.5rem);
  line-height: .95; letter-spacing: -.035em;
  color: var(--c-text); opacity: 0;
  text-wrap: balance;
}
/* A stepped line: several words filling in one at a time and all staying. */
.eg-m-rp__steps {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0 .4em; font-size: clamp(1.8rem, 5.6vw, 3.6rem);
}
/* The supporting caption. It was 12-14px of --c-text-3 at .24em, which is
   the "10px letter-spaced eyebrow" the story pass was supposed to have
   fixed: on a screen recording it was not legible at all, so the picture did
   all the work and the sentence was furniture. Bigger, brighter and less
   tracked out — still clearly secondary to a 38-88px word. */
/* THE SUPPORTING LINE IS A SENTENCE, NOT AN EYEBROW.
   It was 18px of 600-weight uppercase at .13em on the secondary text colour,
   sitting under an 88px display word — a 5:1 size ratio, wide tracking and
   low contrast together, which is why it read on video as the 10px
   letter-spaced eyebrow this system is supposed to have left behind. It is
   the same line of copy; it is simply legible now. Hierarchy is carried by
   size and weight against a 900-weight display word, which is enough. */
.eg-m-rp__sub {
  font-size: clamp(1.0625rem, 1.7vw, 1.375rem); font-weight: 600;
  letter-spacing: .075em; text-transform: uppercase;
  color: var(--c-text); opacity: 0;
}

/* A real progress line, not a percentage: it tracks the sequence that is
   actually playing, and the engine completes it the moment the page is
   ready. Deliberately thin and at the very bottom edge. */
.eg-m-rp__bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: rgb(255 255 255 / 0.10); opacity: 0;
}
.eg-m-rp__bar i {
  display: block; height: 100%; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--eg-violet), var(--eg-blue), var(--eg-teal));
}

/* Stage furniture the variants share. */
/* Supporting furniture: posters, passes, panels. Deliberately quieter than
   the curve, but the first pass was set in the surface colour at 70% on a
   ground only a shade darker, which made them invisible rather than quiet —
   the event-detail entrance in particular read as a bare line on navy. */
.eg-m-rp .eg-m-card {
  position: absolute; border-radius: 8px;
  /* 0.30 left small cards as silhouettes on the navy ground. */
  border: 1.25px solid rgb(255 255 255 / 0.40);
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--eg-violet) 12%, var(--c-surface)),
      color-mix(in srgb, var(--c-teal) 8%, var(--c-surface)));
  will-change: transform, opacity;
  overflow: hidden;
}
/* A card with nothing in it reads as a placeholder rather than as a poster or
   a pass, which is what the loaders are asking the reader to recognise. These
   are the suggestion of a picture and two lines of type — drawn with the
   card's own gradient stack, so there is no extra element per card and
   nothing new to animate. */
.eg-m-rp .eg-m-card::after {
  content: ""; position: absolute; inset: 0;
  background:
    /* two short type lines, low and left */
    linear-gradient(90deg, rgb(255 255 255 / 0.34) 0 42%, transparent 42%)
      bottom 18% left 12% / 62% 2px no-repeat,
    linear-gradient(90deg, rgb(255 255 255 / 0.20) 0 62%, transparent 62%)
      bottom 10% left 12% / 62% 2px no-repeat,
    /* and an image field above them */
    linear-gradient(160deg, rgb(255 255 255 / 0.13), rgb(255 255 255 / 0.02))
      top 10% left 12% / 76% 44% no-repeat;
}
/* The stub stack and the thin bars use the same idea at a smaller scale. */
.eg-m-rp .eg-m-card--lit::after { opacity: .7; }

/* The front card of a stack. A dark card on a dark ground needs an edge and
   a lit face or it is a silhouette — which is exactly what the organisers
   entrance was showing: four near-invisible slabs. */
.eg-m-rp .eg-m-card--lead {
  border-color: rgb(255 255 255 / 0.46);
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--eg-violet) 30%, var(--c-surface)),
      color-mix(in srgb, var(--c-teal) 16%, var(--c-surface)));
  box-shadow: 0 18px 44px rgb(0 0 0 / 0.5);
}

/* The one card per entrance that is the subject rather than the scenery:
   the event-detail stub and the help card. Same gradient edge as the
   flagship ticket, so the family holds. */
.eg-m-rp .eg-m-card--lit {
  border: 0; border-radius: 10px;
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--eg-violet) 18%, var(--c-ink-soft)),
      color-mix(in srgb, var(--c-teal) 12%, var(--c-ink-soft)));
}
.eg-m-rp .eg-m-card--lit::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1.5px;
  background: linear-gradient(96deg, var(--eg-violet), var(--eg-blue), var(--eg-teal));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}

/* The curve. Identical weight and gradient to the cursor's, which is what
   makes a visitor recognise the loader and the pointer as the same object. */
.eg-m-rp__svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
}
.eg-m-rp__line {
  fill: none; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Objects sit in viewport coordinates, the same space the curve is drawn in,
   so a rider is exactly on the line rather than near it. */
.eg-m-rp .eg-m-obj { position: absolute; left: 0; top: 0; opacity: 0; }

.eg-m-rp .eg-m-word {
  position: absolute;
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(1.1rem, 3.4vw, 1.9rem); letter-spacing: -.02em;
  white-space: nowrap; opacity: 0;
}

/* ---- The flagship ticket ------------------------------------------------
   A notched, gradient-edged EventGo admission ticket carrying the lockup.
   The first build made this 300px wide in the deepest ink at 94% — on a
   1440px screen it read as a small grey box with unreadable grey text, which
   is exactly why the flagship entrance looked like a loading placeholder.
   It is now larger, lighter than its ground, and carries the perforation and
   the validation sweep as real elements rather than as a suggestion.
   ---------------------------------------------------------------------- */

.eg-m-rp .eg-m-ticket {
  position: absolute; left: 50%; top: 50%;
  width: min(420px, 82vw); aspect-ratio: 420/236;
  border-radius: 14px; overflow: hidden;
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--eg-violet) 15%, var(--c-ink-soft)) 0%,
      var(--c-ink-soft) 52%,
      color-mix(in srgb, var(--c-teal) 12%, var(--c-ink-soft)) 100%);
  /* 26px, not 15px. The ticket is 421x236 on a 1280 viewport — it was never
     too SMALL — but a 15px notch is 3.6% of its width, and beside a centred
     brand lockup that read as a gift card rather than as admission. The
     notch and the perforation are the only two things that say "ticket". */
  -webkit-mask:
    radial-gradient(circle 26px at 0 50%, transparent 98%, #000 100%),
    radial-gradient(circle 26px at 100% 50%, transparent 98%, #000 100%),
    linear-gradient(#000 0 0);
  -webkit-mask-composite: source-in;
  mask:
    radial-gradient(circle 26px at 0 50%, transparent 98%, #000 100%),
    radial-gradient(circle 26px at 100% 50%, transparent 98%, #000 100%),
    linear-gradient(#000 0 0);
  mask-composite: intersect;
  will-change: transform, opacity;
}
.eg-m-rp .eg-m-ticket::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1.75px;
  background: linear-gradient(96deg, var(--eg-violet), var(--eg-blue), var(--eg-teal));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.eg-m-rp .eg-m-ticket__in {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center; gap: .45rem;
  text-align: center;
}
.eg-m-rp .eg-m-ticket__w {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(1.7rem, 5.4vw, 2.4rem); letter-spacing: -.03em; line-height: 1;
  color: var(--c-text);
}
.eg-m-rp .eg-m-ticket__a {
  font-size: .5625rem; font-weight: 700; letter-spacing: .34em;
  text-transform: uppercase; color: var(--c-text-2);
}
.eg-m-rp .eg-m-ticket__t { font-size: .6875rem !important; }

/* The perforation, scaled open from the centre when the ticket is validated
   so the tear reads as something that HAPPENS rather than as decoration. */
/* The tear line. It was a 1px hairline at 55% and simply did not register
   against the lockup beside it; a perforation a reader cannot see is not
   doing the one job it has. 2px, brighter, and it sits between the notches
   so the three read as one idea. */
.eg-m-rp .eg-m-ticket__perf {
  position: absolute; left: 50%; top: 14px; bottom: 14px; width: 2px;
  transform: translateX(-50%);
  background: repeating-linear-gradient(
    180deg, color-mix(in srgb, var(--c-text) 78%, transparent) 0 5px, transparent 5px 10px);
}
/* The validation sweep. A band of brand light crossing the ticket once. */
.eg-m-rp .eg-m-ticket__scan {
  position: absolute; left: -10%; right: -10%; height: 46%;
  background: linear-gradient(180deg,
    transparent,
    color-mix(in srgb, var(--c-teal) 42%, transparent) 45%,
    color-mix(in srgb, var(--eg-blue) 30%, transparent) 62%,
    transparent);
}

.eg-m-rp .eg-m-line {
  position: absolute; height: 1px; left: 0; right: 0;
  background: linear-gradient(90deg, transparent, var(--eg-blue), transparent);
  opacity: 0; will-change: transform, opacity;
}
.eg-m-rp .eg-m-band {
  position: absolute; border-radius: 4px; will-change: transform, opacity;
}

/* ==========================================================================
   Reduced motion
   --------------------------------------------------------------------------
   No trails, no tap bursts, no assembly, no fan-outs, no doors. The Route Pass
   becomes a short branded fade and the pointer stays native — a trailing
   cursor IS motion and there is no still version of it worth shipping.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .eg-m-trail, .eg-m-touch { display: none !important; }
  html.eg-m-on, html.eg-m-on body, html.eg-m-on a, html.eg-m-on button { cursor: auto; }
  .eg-m-rp__stage, .eg-m-rp__say, .eg-m-rp__bar { display: none; }
}

/* ==========================================================================
   FAILSAFE
   --------------------------------------------------------------------------
   If the timeline never runs — vendor 404, a thrown error, an unforeseen
   browser — the overlay clears anyway. Paired with an inline setTimeout in
   <head> that survives every script failing. A transition that traps the
   visitor is the worst thing this system could do, so it has two independent
   ways out.
   ========================================================================== */

/* IT MUST STAND DOWN WHEN THE ENGINE IS ALIVE.

   THIS WAS THE MOBILE CUT-OFF. Ungated, this animation took the overlay to
   opacity 0 and visibility hidden at exactly six seconds after the class
   landed — whatever the story was doing. On a fast desktop link the home
   entrance finishes at about 4.4s and never reaches it, which is why it
   survived every test and every desktop review. On cellular the scripts
   arrive seconds later, the story is still playing at six seconds, and the
   loader simply VANISHED mid-beat: the page appeared underneath and the
   remaining acts played into an invisible overlay.

   Measured on a 1.2Mbps/150ms profile at 4x CPU: the word "Doors open."
   reached full opacity on its own element and was never once visible,
   because its ancestor had been faded out from under it. Three acts, then
   nothing — exactly the report from a real iPhone.

   `data-rp-live` is set by the engine the moment it takes ownership and
   removed when it settles, so this now covers only the case it was written
   for: the head script ran, the overlay went up, and the motion scripts
   never got far enough to own it. When the engine IS alive its own
   ten-second watchdog is the safety net, and that one knows what state it
   interrupted.

   Eight seconds rather than six, so it cannot race a slow engine start
   even in the moment before the attribute lands. */
html.eg-rp:not([data-rp-live]) .eg-m-rp {
  animation: eg-m-failsafe 0.01s linear 8s forwards;
}
@keyframes eg-m-failsafe { to { opacity: 0; visibility: hidden; pointer-events: none; } }
