/* ═══════════════════════════════════════════════════════════════════════════
   MOTION — «الضوءُ يجدُ المخطوط» / knowledge brought into the light

   ONE LANGUAGE, NOT PER-PAGE EFFECTS. Before this file the site carried 97
   @keyframes blocks and 143 animation declarations, 71 of which never stopped:
   28 of them were running inside the first viewport of the entrance hall and 98
   were running at once on the library shelf — 75 of those below the fold, where
   nobody could see them and every one was still costing a frame. A page that
   never stops moving reads as restless rather than rich, and it competes with
   the one thing this archive exists to present.

   Four durations, three easings, one budget. Everything on the public site is
   built from these, and anything that cannot be expressed in them is asking to
   become a fifth kind of moment — the answer is almost always no.

     ceremonial  --m-scene    900ms   once per page, once per visit
     editorial   --m-settle   420ms   a section taking its place
     functional  --m-quick    180ms   a control answering a press
     instant     --m-instant   90ms   the press itself

   THE BUDGET IS FOUR. Four animations may run in a viewport at once. Ambient
   motion is permitted in exactly one place — the live page, while a broadcast
   is genuinely live — and is prohibited outright behind text being read.

   ARABIC IS NEVER TAKEN APART. No per-letter reveal, no glyph scramble, no
   transform on the text itself: only on the block that carries it, so the joins
   that make the script legible survive the animation. Every reveal starts from
   a frame in which the words can already be read.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── durations ── */
  --m-instant:90ms;
  --m-quick:180ms;
  --m-settle:420ms;
  --m-scene:900ms;

  /* ── easings ──
     Arriving decelerates, leaving accelerates, light glides. Three, because a
     fourth curve is a difference nobody can name and everybody can feel as
     inconsistency. */
  --m-enter:cubic-bezier(.16,.84,.34,1);
  --m-exit:cubic-bezier(.4,0,1,1);
  --m-light:cubic-bezier(.22,1,.36,1);

  /* ── travel ──
     6px reads as weight; 24px reads as a slide, and a slide under Arabic set at
     reading size is what makes a page feel cheap. Halved on a phone, where the
     same distance is a larger share of the screen. */
  --m-lift:6px;
  --m-stagger:70ms;
}

@media (max-width:767.98px){
  :root{ --m-lift:3px; --m-stagger:55ms; }
}

/* ── the site's own tokens, re-pointed at the scale above ───────────────────
   site.css has carried --base / --slow / --reveal / --ease / --ease-in since the
   beginning, and every transition in 2,481 lines of it is written against them.
   Re-aiming those five is what makes one motion language out of the whole site
   in one move: no selector is overridden, nothing is duplicated, and a rule
   written next year against --base still obeys the system.

   This file must therefore load LAST. */
:root{
  --base:var(--m-quick);      /* was 240ms */
  --slow:var(--m-settle);     /* was 480ms */
  --reveal:var(--m-settle);   /* was 700ms — the single biggest source of lag */
  --ease:var(--m-enter);
  --ease-in:var(--m-enter);
}

/* ═══ THE SIGNATURE ═════════════════════════════════════════════════════════
   One pass of light across the entrance hall, and then it is over. It is a
   mask travelling over the scene, not a glow that stays behind: the scene it
   reveals is the resting state, so the reader who arrives after it has finished
   has missed nothing at all.

   It runs once per visit. `js/motion.js` marks the document when the ceremony
   has been spent, and the hall opens still for the rest of the session. */
.hero .hs-light{
  position:absolute;inset:-12% -35%;z-index:1;pointer-events:none;
  opacity:0;
  background:linear-gradient(105deg,
    transparent 34%,
    rgba(255,247,226,.26) 47%,
    rgba(255,247,226,.06) 56%,
    transparent 66%);
  will-change:transform,opacity;
}
html[data-world="manuscript"] .hero .hs-light{
  background:linear-gradient(105deg,
    transparent 34%,
    rgba(255,252,240,.85) 47%,
    rgba(255,250,232,.30) 56%,
    transparent 66%);
}

/* The gilded margin: the archive's own way of saying «a chapter begins here». */
.hero .hs-margin{
  position:absolute;inset-block:14%;inset-inline-start:0;width:2px;z-index:1;
  background:linear-gradient(180deg,transparent,rgba(212,176,106,.85),transparent);
  transform:scaleY(0);transform-origin:50% 0;
}

/* ── the ceremony, played once ──
   Ordered so the words are the FIRST thing to finish, not the last: the ground
   settles under them while they are already readable. */
html[data-entrance="play"] .hero .hs-light{
  animation:mLightPass 1250ms var(--m-light) 200ms both;
}
html[data-entrance="play"] .hero .hs-margin{
  animation:mMarginDraw 620ms var(--m-enter) 120ms both;
}
/* THE WORDS ARE NOT PART OF THE CEREMONY.
   This first animated .hero__inner from opacity 0 — and measured in the browser, the
   quotation was invisible for the whole opening while the light crossed an empty
   scene. That is the exact failure the light was meant to avoid: a reader arriving
   mid-animation, or on a machine where the animation stalls, waits for words that are
   already downloaded and rendered.

   So the hero holds still and stays legible from its first frame. The light passes
   OVER text that is already readable, which is what «الضوءُ يجدُ المخطوط» describes —
   the light finds the manuscript; it does not deliver it. The scene now has exactly
   two moving parts, both of them decorative, and neither carries any content. */

@keyframes mLightPass{
  0%{opacity:0;transform:translateX(-38%)}
  22%{opacity:1}
  100%{opacity:0;transform:translateX(42%)}
}
@keyframes mMarginDraw{from{transform:scaleY(0)}to{transform:scaleY(1)}}
}

/* ═══ EDITORIAL ═════════════════════════════════════════════════════════════
   `.reveal` / `.reveal.in` already exists in site.css and home.css and is driven
   by the observer in js/site.js. It is not redefined here — re-pointing --reveal
   above already gave it the new duration and curve. What IS corrected here is
   the travel: 16–20px was a slide, and a slide under Arabic at reading size is
   what made the page feel like a template. */
.reveal{transform:translateY(var(--m-lift))}

/* THE HERO IS EXEMPT, and always visible. home.css had `.hero .reveal{opacity:0}`
   released by `body.revealed`, which home.js adds — so the masthead of the most
   visited page on the site depended on a script running, and faded in over 900ms
   when it did. It is the first thing a reader sees and it is already rendered;
   there is nothing to reveal. The light below is the entrance. */
.hero .reveal{opacity:1;transform:none;transition:none}

/* The stagger stops at the third item. js/site.js used `i % 4 * 60ms`, which
   both cycles — so item 5 starts before item 4 — and grows without limit inside
   each group of four. A reader perceives the first three as a cascade and
   everything after as a wait. */
.reveal{transition-delay:calc(min(var(--i,0),2) * var(--m-stagger))}

/* ═══ THE CREST, AT REST ════════════════════════════════════════════════════
   The mark carried about twenty endless loops — two rotations, a counter-
   rotation, a breath, a float, six drifting sparks, a travelling sheen, a
   glint, and four pulses on its gems and gilding. All of them are gone.

   STILL IS NOT STRIPPED. Three of those parts were drawn at opacity 0 and were
   visible only part-way through their own loop, so stopping the loop would have
   deleted them rather than settled them — the crest would have lost its gilding
   and looked flat, which is a worse outcome than the motion was. They are given
   the value they used to pass through, and they hold it.

     .e-gild   pulsed .08 → .42     rests at .22
     .e-spec   flashed 0 → .8       rests at .30, a fixed glint
     .e-sheen  swept 0 → .34        stays 0: a sheen is only a sheen in motion,
                                    and a stationary one is just a bright band
     .e-parts circle                stays 0: six sparks that do not drift are
                                    six dots */
.e-gild{opacity:.22}
.e-spec{opacity:.30}

/* ═══ FUNCTIONAL ════════════════════════════════════════════════════════════
   Controls answer in 180ms and are interruptible at every moment. Nothing on
   this site makes a reader wait for an exit animation before they may act. */
.btn,.b,.chip,.card__link,.lnk,.icobtn,.wsearch__go,.pager a,.pager b{
  transition:background var(--m-quick) var(--m-enter),
             border-color var(--m-quick) var(--m-enter),
             color var(--m-quick) var(--m-enter),
             transform var(--m-instant) var(--m-exit);
}
.btn:active,.b:active,.chip:active,.icobtn:active{transform:scale(.97)}

/* ═══ AMBIENT ═══════════════════════════════════════════════════════════════
   The whole permitted allowance for the public site is one effect: `liveBeat`,
   on the three marks that mean a broadcast is happening right now. It is
   defined in site.css and is NOT restated here — two rules animating the same
   box-shadow on the same element is a fight, not a system — and it is already
   gated correctly: `.is-live .live-nav-dot` on the class, `.liveBar__dot` and
   `.lp__badge i` on markup the server only renders while a broadcast is on.
   An idle live page therefore has nothing pulsing on it, which is the point.

   Everything else that used to breathe, drift, orbit, spin, shimmer or float
   was removed from the stylesheets rather than paused here, so it cannot come
   back by being unpaused. What remains endless on the public site, besides the
   live marks, is three honest waits: the loader, the list skeleton and the
   reader's skeleton. */

/* ═══ REDUCED MOTION ════════════════════════════════════════════════════════
   NOT «animations off». Every scene still reaches exactly the same readable end
   state; it reaches it immediately, by opacity alone, with no travel and no
   light. The one thing that must never happen is an element left at opacity 0
   because the animation that would have revealed it did not run. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms!important;
    animation-delay:0ms!important;
    animation-iteration-count:1!important;
    transition-duration:1ms!important;
    transition-delay:0ms!important;
    scroll-behavior:auto!important;
  }
  .hero .hs-light{display:none}
  .hero .hs-margin{transform:scaleY(1)}
  .reveal{opacity:1;transform:none}
}

/* THE LAST LINE OF DEFENCE. If script never runs — it failed, it was blocked,
   it is still downloading on a slow connection — nothing on this site may stay
   invisible. `js/motion.js` sets `data-motion="on"` on <html> as its first act;
   until then, and for ever if it never runs, every reveal is simply visible. */
html:not([data-motion="on"]) .reveal{opacity:1;transform:none}

/* ═══ THE FEATURED CAROUSEL ═════════════════════════════════════════════════
   The progress bar used to be `updateProgress 6.5s linear infinite` — a loop
   that ran whether or not the carousel was advancing, and that stated a reading
   time of six and a half seconds for every item alike. One of the three items
   on the live homepage is a ninety-character question; nobody reads that in six
   and a half seconds, and a quotation that changes under a reader is one they
   will not finish.

   The bar is finite now, and its duration is the slide's own: js/home.js
   measures the text and sets --dwell. It runs once per slide, which is exactly
   as often as there is something to time. */
.hero-updates__progress i{
  transform:scaleX(0);transform-origin:inline-start;
  animation:mDwell var(--dwell,9s) linear 1 both;
}
@keyframes mDwell{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@media (prefers-reduced-motion:reduce){
  .hero-updates__progress i{animation:none;transform:scaleX(0)}
}
