/* =================================================================
   amirr.me — one screen, no scroll.

   Layer order, back to front:
     1  #smoke    WebGL canvas, fixed, decorative
     2  .stage    the composition (wordmark, rig, player)
     3  .gate     the black door that opens on click
   ================================================================= */

/* The wordmark face: Corleone by Michael Hagemann (FontMesa, 2001),
   freeware — https://www.fontspace.com/corleone-font-f3163. This is the
   cut with the extended top line on capitals, which is what the movie
   logo and the reference gif use. Display only; it is unreadable as UI
   text, so Big Shoulders still carries every small label. */
@font-face {
  font-family: 'Corleone';
  src: url('../assets/fonts/corleone-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Big Shoulders Display';
  src: url('../assets/fonts/big-shoulders-display-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

*, *::before, *::after { box-sizing: border-box; }

:root {
  /* --- colour -------------------------------------------------- */
  /* Drenched black. The smoke is the only "image", the cyan is the
     only colour, and it is rationed: motes, focus, volume fill. */
  --void:    oklch(0.055 0.008 240);
  --ink:     oklch(0.970 0.006 220);
  --ink-2:   oklch(0.800 0.008 220);
  --ink-3:   oklch(0.640 0.010 220);   /* 4.9:1 on --void */
  --ember:   oklch(0.840 0.130 195);   /* the cyan in the smoke */
  --ember-d: oklch(0.640 0.110 195);

  /* --- type ---------------------------------------------------- */
  --face: 'Big Shoulders Display', 'Oswald', 'Arial Narrow', sans-serif;
  --face-display: 'Corleone', 'Big Shoulders Display', 'Arial Narrow', sans-serif;

  /* --- rhythm -------------------------------------------------- */
  /* Caps are deliberately loose: on a 1440-tall display a composition
     that stops growing at 7rem reads as a small island on a big black
     field, which is the opposite of the intent. */
  --head:      clamp(3.75rem, 30vh, 22rem);   /* room above the wordmark for the mark */
  --icon-row:  clamp(4.6rem, 14vh, 9rem);     /* reserved slot the icons hang into */
  --gap-1:     clamp(1.1rem, 4.5vh, 3.6rem);  /* wordmark → icons */
  --gap-2:     clamp(1rem, 3.6vh, 3rem);      /* icons → player */
  --pad:       clamp(1rem, 3vw, 2.5rem);

  /* Cracks. fractalNoise pushed through a one-slot-in-31 discrete notch,
     which turns a cloud into thin winding fault lines. stitchTiles keeps
     the seams invisible when it repeats. White keeps, black cuts. */
  /* Cracks. fractalNoise pushed through a one-slot-in-61 discrete notch.
     The notch got narrower and the tile smaller when the wordmark moved to
     Corleone: its strokes are a fraction of the width of the 800-weight
     gothic that was here before, and the old texture severed them. */
  --crack-tex: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='460' height='460'><filter id='c' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.019' numOctaves='5' seed='9' stitchTiles='stitch'/><feColorMatrix values='1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1'/><feComponentTransfer><feFuncR type='discrete' tableValues='1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'/><feFuncG type='discrete' tableValues='1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'/><feFuncB type='discrete' tableValues='1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'/></feComponentTransfer></filter><rect width='460' height='460' filter='url(%23c)'/></svg>");

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);           /* quart-ish */
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);

  --z-stage: 1;
  --z-player: 2;
  --z-gate: 3;
}

html, body { margin: 0; padding: 0; height: 100%; }

body {
  background: var(--void);
  color: var(--ink);
  font-family: var(--face);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;                 /* one screen. never scrolls. */
  overscroll-behavior: none;
}

/* =================================================================
   1 — smoke
   ================================================================= */

#smoke {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  /* the canvas renders below device resolution and is stretched;
     smoke is blurry, nobody can tell, and it costs a third as much */
}

/* If WebGL is unavailable the canvas stays blank — these drifting
   blobs stand in so the page is never a flat black rectangle. */
.no-webgl #smoke { display: none; }
.no-webgl body::before,
.no-webgl body::after {
  content: '';
  position: fixed;
  inset: -20%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(38vmax 26vmax at 22% 78%, rgba(226, 233, 238, 0.16), transparent 62%),
    radial-gradient(30vmax 22vmax at 74% 24%, rgba(210, 220, 228, 0.11), transparent 64%),
    radial-gradient(44vmax 30vmax at 52% 108%, rgba(232, 238, 242, 0.13), transparent 60%);
  filter: blur(28px);
  animation: drift 44s ease-in-out infinite alternate;
}
.no-webgl body::after { animation-duration: 63s; animation-direction: alternate-reverse; opacity: .7; }

@keyframes drift {
  from { transform: translate3d(-3%, 2%, 0) scale(1); }
  to   { transform: translate3d(4%, -3%, 0) scale(1.14); }
}

/* =================================================================
   2 — the stage
   ================================================================= */

.stage {
  position: relative;
  z-index: var(--z-stage);
  height: 100svh;
  height: 100dvh;
  display: grid;
  /* head · wordmark · gap · icons · gap · player */
  grid-template-rows: var(--head) auto var(--gap-1) var(--icon-row) var(--gap-2) auto;
  row-gap: 0;
  align-content: center;
  justify-items: center;
  padding: max(var(--pad), env(safe-area-inset-top))
           var(--pad)
           max(var(--pad), env(safe-area-inset-bottom));
}

/* ---- the wordmark ------------------------------------------------ */

.wordmark {
  grid-row: 2;
  position: relative;
  z-index: 3;               /* strings pass behind the letters */
  margin: 0;
  display: grid;
  justify-items: start;
  font-family: var(--face-display);
  font-weight: 400;            /* Corleone is one weight; faux-bold wrecks it */
  line-height: 0.9;
  color: var(--ink);

  /* keeps white type readable wherever a bright plume drifts behind */
  filter: drop-shadow(0 0.06em 0.5em rgba(0, 0, 0, 0.85));
}

.wordmark__slab {
  display: grid;
  justify-items: start;

  /* The slab owns the size; both lines are ems of it. That is what lets
     the crack tile be sized in em too, so the fault lines stay the same
     size *relative to the letterforms* at every viewport instead of
     shredding the type on a small screen. */
  font-size: clamp(3rem, min(19vw, 14.5vh), 10.5rem);

  -webkit-mask-image: var(--crack-tex);
  mask-image: var(--crack-tex);
  -webkit-mask-size: 2.5em 2.5em;
  mask-size: 2.5em 2.5em;
  -webkit-mask-mode: luminance;
  mask-mode: luminance;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}

.wordmark__welcome {
  /* mixed case and tight, exactly as the reference sets it — not the
     tracked-out uppercase label this used to be */
  font-size: clamp(0.95rem, min(3.6vw, 3vh), 1.7rem);
  letter-spacing: 0.02em;
  color: var(--ink);
  margin-left: 0.12em;
  margin-bottom: 0.06em;
}

.wordmark__name {
  font-size: 1em;
  letter-spacing: 0.004em;
  white-space: nowrap;
}

.wordmark__suffix {
  /* tucked under the tail of the name and pushed right, the way the
     reference hangs "profile" off the end of the logo */
  justify-self: end;
  font-size: 0.4em;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin-top: -0.12em;
  margin-right: 0.06em;
}

/* ---- the rig (hand, bar, strings, icons) ------------------------- */

.rig {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;           /* only the icons opt back in */
}

.rig svg { display: block; width: 100%; height: 100%; overflow: visible; }

.rig__mark { fill: var(--ink); }

/* stroke-width and opacity are set per string in rig.js, so that no two
   read as the same machine-drawn line */
.rig__string {
  stroke: var(--ink-2);
  fill: none;
  stroke-linecap: round;
}

.marionette { pointer-events: auto; cursor: pointer; }

.marionette__hit { fill: transparent; }

.marionette__disc {
  fill: rgba(10, 12, 14, 0.55);
  stroke: rgba(226, 233, 238, 0.22);
  stroke-width: 1;
  transition: fill 0.35s var(--ease-out), stroke 0.35s var(--ease-out);
}

.marionette__glyph {
  fill: var(--ink-2);
  transition: fill 0.35s var(--ease-out);
}

.marionette__label {
  fill: var(--ink-3);
  font-family: var(--face);
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-anchor: middle;
  opacity: 0;
  transition: opacity 0.3s var(--ease-out);
}

.marionette:hover .marionette__glyph,
.marionette:focus-visible .marionette__glyph { fill: var(--ink); }

.marionette:hover .marionette__disc,
.marionette:focus-visible .marionette__disc {
  fill: rgba(18, 26, 30, 0.7);
  stroke: rgba(126, 226, 222, 0.5);
}

.marionette:hover .marionette__label,
.marionette:focus-visible .marionette__label { opacity: 1; }

.marionette:focus { outline: none; }
.marionette:focus-visible .marionette__ring {
  stroke: var(--ember);
  stroke-width: 1.6;
  opacity: 1;
}
.marionette__ring { fill: none; stroke: transparent; opacity: 0; }

.marionette--copied .marionette__disc { stroke: var(--ember); }
.marionette--copied .marionette__glyph { fill: var(--ember); }

/* =================================================================
   the player
   ================================================================= */

/* empty, but it reserves the band the icons hang into and gives JS a
   real box to measure instead of guessing at grid maths */
.icon-slot { grid-row: 4; width: min(42rem, 100%); height: 100%; }

.player {
  grid-row: 6;
  z-index: var(--z-player);
  display: flex;
  align-items: center;
}

.player[hidden] { display: none; }

/* =================================================================
   Elastic Slider
   after Build UI's recipe (buildui.com/recipes/elastic-slider),
   rebuilt without React or Framer Motion.
   ================================================================= */

.slider {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: clamp(11rem, 30vw, 15rem);
  touch-action: none;             /* we own the drag */

  /* room for the 1.2x zoom to grow into rather than off the edge */
  margin-inline: clamp(0.4rem, 1.8vw, 1rem);

  /* the whole control recedes until touched, then zooms — straight from
     THE LAB's component, where the wrapper animates scale 1 → 1.2 and
     opacity 0.7 → 1 together */
  --zoom: 1.2;
  opacity: 0.7;
  transform: scale(1);
  transition: opacity 0.28s var(--ease-out), transform 0.28s var(--ease-out);
}

.slider:hover,
.slider:focus-within,
.slider--active {
  opacity: 1;
  transform: scale(var(--zoom));
}

/* On a phone the control is already almost the full width of the screen,
   so there is nowhere for a 1.2x zoom to grow except off the edge — it
   pushed 38px of horizontal overflow at 320px. The fade and the 6→12px
   track still carry the interaction. */
@media (max-width: 30rem) {
  .slider { --zoom: 1; }
}

.slider__icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.05rem;
  height: 1.05rem;
  color: var(--ink-3);
  /* pushed outward by however far past the end you have dragged */
  transform: translateX(var(--push, 0px));
  transition: color 0.3s var(--ease-out);
}

/* a single pop the moment you cross an end, not a held scale */
.slider__icon--pop { animation: icon-pop 0.25s var(--ease-out); }

@keyframes icon-pop {
  0%   { scale: 1; }
  50%  { scale: 1.4; }
  100% { scale: 1; }
}

.slider__icon svg { width: 100%; height: 100%; fill: currentColor; }
.slider__icon svg path[stroke] { fill: none; }

.slider:hover .slider__icon,
.slider--active .slider__icon { color: var(--ink-2); }

/* the pointer-grabbing area is taller than the visible track so the
   6px line is not a 6px target */
.slider__region {
  position: relative;
  flex: 1;
  height: 1.5rem;
  display: grid;
  align-items: center;
  cursor: pointer;
}

.slider__track {
  position: relative;
  height: 6px;
  border-radius: 999px;
  background: rgba(226, 233, 238, 0.18);
  overflow: hidden;
  transform: scaleX(var(--sx, 1)) scaleY(var(--sy, 1));
  transform-origin: var(--origin, center);
  transition: height 0.28s var(--ease-out);
}

.slider:hover .slider__track,
.slider--active .slider__track { height: 12px; }

.slider__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--v, 0.55) * 100%);
  background: var(--ember);
  border-radius: inherit;
}

.slider__region:focus { outline: none; }
.slider__region:focus-visible { outline: 2px solid var(--ember); outline-offset: 6px; border-radius: 4px; }

/* =================================================================
   3 — the gate
   ================================================================= */

.gate {
  /* Hidden until JS has actually built the slider. The slider is the only
     way in, so a gate without one is a blank sheet over the whole page —
     if the script never runs, there must be no gate at all and the page
     is simply readable without music. */
  display: none;
}

.gate--armed {
  position: fixed;
  inset: 0;
  z-index: var(--z-gate);
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 1.05rem;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  /* Not opaque. The gate hides the *composition* (see `.waiting` below),
     not the smoke — the smoke should already be rolling when you arrive,
     exactly as it does in the reference before the mark appears. This is
     only a scrim, dark enough that "come in" reads over a bright plume. */
  background: radial-gradient(72vmax 58vmax at 50% 46%,
              rgba(6, 7, 10, 0.55), rgba(5, 6, 9, 0.88) 72%);
  color: var(--ink);
  font-family: inherit;
  transition: opacity 0.75s var(--ease-out-expo);
}

.gate__slider { display: grid; place-items: center; }

/* ---- Slide to Confirm -------------------------------------------
   THE LAB's component. It is token-driven, so the tokens are mapped to
   this page's palette here rather than the Lab's — `tone="accent"` means
   "the accent of wherever it is living", and on this page that is the
   cyan in the smoke, not the Lab's gold.
   ------------------------------------------------------------------ */

.stc {
  --stc-accent: var(--ember);
  --stc-border: rgba(226, 233, 238, 0.14);
  --stc-surface: rgba(12, 14, 18, 0.55);
  --stc-raised: rgba(30, 36, 42, 0.96);
  --stc-fg-dim: rgba(226, 233, 238, 0.34);
  --stc-fg-strong: var(--ink);
  --stc-fg-muted: var(--ink-3);

  font-family: var(--face);
}

.stc[data-tone='danger']  { --stc-accent: oklch(0.66 0.19 25); }
.stc[data-tone='neutral'] { --stc-accent: var(--ink); }

.stc__track {
  position: relative;
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid var(--stc-border);
  background: var(--stc-surface);
  backdrop-filter: blur(2px);
}

/* the label sits behind the knob, clear of its resting position */
.stc__label {
  position: absolute;
  inset-inline-end: 12px;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.86rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  user-select: none;
  transition: opacity 0.16s linear;
}

/* The one gradient-on-text on this site. It is not decoration — the sweep
   is what tells you the control is draggable, and it is the component's
   identity upstream. */
/* The sweep has to travel exactly one tile, or the loop cannot seam.
 *
 * `background-repeat` is unset, so it is `repeat`: the gradient tiles every
 * 220% of the box. A background-position percentage `p` resolves to an
 * offset of `(box - image) * p`, i.e. `-1.2 * box * p` here — so the
 * inherited 120% → -120% travels 2.88 boxes against a 2.2-box tile. It
 * lands mid-tile, and the restart catches a highlight halfway across the
 * word: one partial sweep, a dead gap, then a full one.
 *
 * 2.2 / 1.2 = 1.8333, so 100% → -83.333% moves exactly one tile and the
 * first and last frames are identical.
 */
.stc__shimmer {
  background-image: linear-gradient(90deg,
    var(--stc-fg-dim) 0%, var(--stc-fg-dim) 35%,
    var(--stc-fg-strong) 50%,
    var(--stc-fg-dim) 65%, var(--stc-fg-dim) 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: stc-shimmer linear infinite;
}

@keyframes stc-shimmer {
  from { background-position: 100% 0; }
  to   { background-position: -83.3333% 0; }
}

/* the confirmed state renders in the same box, so nothing shifts */
.stc__done {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-size: 0.86rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--stc-fg-strong);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s var(--ease-out);
}

.stc--confirmed .stc__done,
.stc--pending .stc__done { opacity: 1; }
.stc--confirmed .stc__label,
.stc--pending .stc__label { opacity: 0; }

.stc__spinner { display: none; }
.stc--pending .stc__spinner {
  display: block;
  width: 13px;
  height: 13px;
  border-radius: 999px;
  border: 2px solid var(--stc-fg-dim);
  border-top-color: var(--stc-fg-strong);
  animation: stc-spin 900ms linear infinite;
}

@keyframes stc-spin { to { rotate: 360deg; } }

.stc__knob {
  position: absolute;
  top: 4px;
  inset-inline-start: 4px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--stc-raised);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.4);
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  outline: none;
}

.stc--dragging .stc__knob { cursor: grabbing; }
.stc__knob svg { stroke: var(--stc-accent); }
.stc__knob:focus-visible { outline: 2px solid var(--ember); outline-offset: 3px; }

.gate:focus-visible { outline: 2px solid var(--ember); outline-offset: -6px; }

.gate--open {
  opacity: 0;
  pointer-events: none;
}

/* =================================================================
   entrance choreography
   Everything below is the default *hidden* state, and it only applies
   while `.waiting` sits on <html> — put there by the inline script in the
   head, since that is the last moment before the first paint and a module
   is always later than that. Content is visible by default otherwise, so
   JS off, a headless render, or a stalled transition can never ship a
   blank page.
   ================================================================= */

.waiting .wordmark__welcome,
.waiting .wordmark__name,
.waiting .wordmark__suffix,
.waiting .rig,
.waiting .player { opacity: 0; }

.waiting .wordmark__name { letter-spacing: 0.16em; }

.entering .wordmark__welcome,
.entering .wordmark__name,
.entering .wordmark__suffix {
  animation: condense 1.5s var(--ease-out-expo) both;
}
.entering .wordmark__welcome { animation-delay: 0.36s; }
.entering .wordmark__name    { animation-delay: 0.1s; animation-name: condense-name; }
.entering .wordmark__suffix  { animation-delay: 0.58s; }

/* The two small lines get a gentler blur than the big one. A 16px blur
   on 40px text smears into a flat grey rectangle the size of the filter
   region — it reads as a box, not as smoke. */
.entering .wordmark__welcome,
.entering .wordmark__suffix { --blur-in: 7px; }
.entering .wordmark__name   { --blur-in: 22px; }

/* The rig arrives top-down, so the strings read as being lowered rather
   than just fading in. The mask is 2.6x the element, so sliding it from
   bottom to top wipes the visible band downward. */
.entering .rig {
  opacity: 1;
  -webkit-mask-image: linear-gradient(to bottom, #000 40%, transparent 58%);
  mask-image: linear-gradient(to bottom, #000 40%, transparent 58%);
  -webkit-mask-size: 100% 260%;
  mask-size: 100% 260%;
  animation: descend 1.15s var(--ease-out) 0.5s both;
}

@keyframes descend {
  from { -webkit-mask-position: 0% 100%; mask-position: 0% 100%; }
  to   { -webkit-mask-position: 0% 0%;   mask-position: 0% 0%; }
}

.entering .player { animation: rise 0.9s var(--ease-out) 1.55s both; }

@keyframes condense {
  from { opacity: 0; filter: blur(var(--blur-in, 8px)); transform: scale(1.06); }
  60%  { opacity: 1; }
  to   { opacity: 1; filter: blur(0); transform: scale(1); }
}

@keyframes condense-name {
  from { opacity: 0; filter: blur(var(--blur-in, 22px)); letter-spacing: 0.16em; transform: scale(1.05); }
  55%  { opacity: 1; }
  to   { opacity: 1; filter: blur(0); letter-spacing: -0.012em; transform: scale(1); }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(0.9rem); }
  to   { opacity: 1; transform: translateY(0); }
}

/* =================================================================
   short and wide — landscape phones, the tightest case
   ================================================================= */

@media (max-height: 460px) and (orientation: landscape) {
  :root {
    --head:     clamp(2.6rem, 16vh, 4rem);
    --icon-row: clamp(3.4rem, 15vh, 4.4rem);
    --gap-1:    0.5rem;
    --gap-2:    0.5rem;
    --pad:      0.75rem;
  }
  .wordmark__slab { font-size: clamp(2rem, min(9vw, 15vh), 3.4rem); }
  .wordmark__welcome { font-size: 0.62rem; letter-spacing: 0.3em; margin-bottom: 0.2em; }
}

/* =================================================================
   accessibility
   ================================================================= */

.live {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  .gate,
  .stc__label,
  .stc__done,
  .marionette__disc,
  .marionette__glyph,
  .marionette__label,
  .slider__icon,
  .slider__track,

  /* The shimmer goes, but the control keeps working — the whole reason
     this component was reworked in the Lab was that upstream disabled
     the drag under reduced motion and left no other way in. */
  .stc__shimmer {
    animation: none;
    background-image: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: var(--stc-fg-muted);
  }

  .stc--pending .stc__spinner { animation: none; }

  .slider,
  .slider:hover,
  .slider:focus-within,
  .slider--active { transform: none; }
  .slider__icon--pop { animation: none; }

  .entering .wordmark__welcome,
  .entering .wordmark__name,
  .entering .wordmark__suffix,
  .entering .rig,
  .entering .player {
    animation: fade-only 0.3s linear both;
    animation-delay: 0s;
  }

  .entering .rig { -webkit-mask-image: none; mask-image: none; }

  .no-webgl body::before,
  .no-webgl body::after { animation: none; }

  @keyframes fade-only { from { opacity: 0; } to { opacity: 1; } }
}
