/* ==========================================================================
   TProM Team — THE INSTRUMENT
   Ambient sky · chrome · hero orrery · transmission · horizon footer

   This file owns everything a visitor meets first: the sky the site sits in,
   the header rail, the hero, the section ornament and the footer. It loads
   after layout.css and deliberately replaces the chrome that used to live
   there.

   Three rules held throughout:
     1. Only transform, opacity and clip-path are animated. Nothing animated
        triggers layout, and no filter is animated per frame.
     2. Every ornament is aria-hidden and pointer-events:none. The instrument
        is scenery; the content underneath is the site.
     3. Logical properties for anything that mirrors in Arabic — except the
        comet and the orrery nodes, which are positioned from measured
        physical geometry and must not be flipped twice.
   ========================================================================== */

/* ══ THE SKY ══════════════════════════════════════════════════════════════
   A fixed backdrop behind every page: void gradient, two drifting nebulae, a
   canvas star field (js/cosmos.js) and a band of light at the bottom. Body
   loses its own background so the negative-z layer can show through. */

body {
  background: transparent;
}

.sky {
  position: fixed;
  inset: 0;
  /* A phone's address bar retracts as you scroll, and the strip it uncovers
     at the bottom is taller than the layout viewport that `inset: 0` sizes
     this to. For those frames the sky simply stopped, and what showed
     through was the bare page background — a black band that filled itself
     in the moment scrolling stopped and layout caught up. `lvh` is the
     viewport at its largest, with the bar already gone, so the sky covers
     that strip before it is ever uncovered. Browsers without `lvh` keep the
     `inset: 0` height above and are no worse off than before. */
  block-size: 100lvh;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  background:
    radial-gradient(120% 80% at 50% -20%, rgba(60, 30, 120, 0.5), transparent 60%),
    radial-gradient(90% 60% at 100% 100%, rgba(120, 40, 140, 0.18), transparent 65%),
    var(--void);
}

.sky__stars {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  opacity: 0.92;
}

/* Static star field for coarse pointers, where the canvas is never started.
   Seven fixed dots per tile cost nothing after first paint. */
.sky__static {
  position: absolute;
  inset: 0;
  opacity: 0;
  background-image:
    radial-gradient(1.4px 1.4px at 20% 32%, rgba(255, 255, 255, 0.75), transparent 60%),
    radial-gradient(1.2px 1.2px at 68% 12%, rgba(217, 184, 255, 0.7), transparent 60%),
    radial-gradient(1.6px 1.6px at 84% 58%, rgba(255, 255, 255, 0.55), transparent 60%),
    radial-gradient(1.1px 1.1px at 12% 76%, rgba(255, 215, 154, 0.55), transparent 60%),
    radial-gradient(1.3px 1.3px at 45% 88%, rgba(255, 255, 255, 0.5), transparent 60%),
    radial-gradient(1px 1px at 33% 54%, rgba(255, 111, 216, 0.5), transparent 60%),
    radial-gradient(1.2px 1.2px at 92% 26%, rgba(255, 255, 255, 0.6), transparent 60%);
  background-size: 46vmax 46vmax;
}
.sky--static .sky__static {
  opacity: 1;
}

.sky__neb {
  position: absolute;
  inline-size: 62vmax;
  block-size: 62vmax;
  border-radius: 50%;
  will-change: transform;
}
.sky__neb--a {
  inset-block-start: -22vmax;
  inset-inline-start: -14vmax;
  background: radial-gradient(circle, rgba(109, 58, 212, 0.34), rgba(109, 58, 212, 0) 62%);
  animation: neb-a 46s var(--ease-inout) infinite alternate;
}
.sky__neb--b {
  inset-block-end: -26vmax;
  inset-inline-end: -18vmax;
  background: radial-gradient(circle, rgba(255, 111, 216, 0.2), rgba(255, 111, 216, 0) 62%);
  animation: neb-b 62s var(--ease-inout) infinite alternate;
}

@keyframes neb-a {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  to {
    transform: translate3d(8vw, 6vh, 0) scale(1.16);
  }
}
@keyframes neb-b {
  from {
    transform: translate3d(0, 0, 0) scale(1.1);
  }
  to {
    transform: translate3d(-9vw, -5vh, 0) scale(1);
  }
}

.sky__glow {
  position: absolute;
  inset-inline: -10%;
  inset-block-end: -30vh;
  block-size: 60vh;
  background: radial-gradient(50% 60% at 50% 100%, rgba(178, 91, 240, 0.16), transparent 70%);
}

.grain {
  opacity: 0.022;
}

/* Reading progress: a hairline of aurora with a bright head. */
.progress {
  block-size: 2px;
  background: var(--aurora);
  box-shadow: 0 0 14px rgba(178, 91, 240, 0.7);
}

/* ── The spark ────────────────────────────────────────────────────────────
   A four-point star trailing the cursor. It leans with velocity and opens
   into a ring over anything interactive. Inserted by script on fine
   pointers only. */
.spark {
  position: fixed;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 130;
  inline-size: 26px;
  block-size: 26px;
  margin-inline-start: -13px;
  margin-block-start: -13px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  will-change: transform;
}
.spark.is-live {
  opacity: 1;
}
.spark__core {
  position: absolute;
  inset: 0;
  background: var(--violet-bright);
  clip-path: polygon(50% 0%, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0% 50%, 42% 42%);
  transform: scale(0.5);
  transition: transform 0.45s var(--ease-orbit), background-color 0.4s var(--ease);
  filter: drop-shadow(0 0 6px rgba(217, 184, 255, 0.9));
}
.spark__ring {
  position: absolute;
  inset: -7px;
  border: 1px solid rgba(217, 184, 255, 0.5);
  border-radius: 50%;
  transform: scale(0.35);
  opacity: 0;
  transition: transform 0.5s var(--ease-orbit), opacity 0.4s var(--ease);
}
.spark.is-near .spark__core {
  transform: scale(0.34) rotate(45deg);
  background: var(--gold);
}
.spark.is-near .spark__ring {
  transform: scale(1);
  opacity: 1;
}

/* ══ PRIMITIVES ═══════════════════════════════════════════════════════════ */

/* Section labels read as star coordinates. */
.eyebrow {
  color: var(--paper-muted);
  letter-spacing: 0.2em;
}
.eyebrow::before {
  content: '✦';
  inline-size: auto;
  block-size: auto;
  border-radius: 0;
  background: none;
  color: var(--violet);
  font-size: 0.9em;
  line-height: 1;
  animation: twinkle 4.5s var(--ease-inout) infinite;
}
@keyframes twinkle {
  0%,
  100% {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
  50% {
    opacity: 0.45;
    transform: scale(0.82) rotate(45deg);
  }
}

/* Emphasis in display type is cut from the aurora. */
.display em {
  background: var(--aurora);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 22px rgba(178, 91, 240, 0.28));
}
@supports not ((background-clip: text) or (-webkit-background-clip: text)) {
  .display em {
    color: var(--violet-bright);
    -webkit-text-fill-color: currentColor;
  }
}
html[lang='ar'] .display em {
  filter: none;
}

/* ── Buttons ────────────────────────────────────────────────────────────
   Solid is a slice of the aurora with a sheen that crosses on hover; ghost
   is a hairline that catches light from below. */
.btn--solid {
  background: var(--aurora);
  background-size: 220% 100%;
  background-position: 12% 50%;
  color: #0a0616;
  font-weight: 600;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12) inset, 0 10px 34px rgba(150, 60, 220, 0.32);
  transition: background-position 0.8s var(--ease), box-shadow 0.5s var(--ease),
    transform var(--dur-fast) var(--ease);
}
.btn--solid::before {
  background: linear-gradient(
    100deg,
    transparent 20%,
    rgba(255, 255, 255, 0.55) 46%,
    rgba(255, 255, 255, 0.1) 60%,
    transparent 78%
  );
  transform: translateX(-115%);
  transition: transform 0.9s var(--ease);
  mix-blend-mode: screen;
}
.btn--solid:hover {
  color: #0a0616;
  background-position: 88% 50%;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2) inset, 0 14px 44px rgba(178, 91, 240, 0.45);
}
.btn--solid:hover::before {
  transform: translateX(115%);
}
html[dir='rtl'] .btn--solid::before {
  transform: translateX(115%);
}
html[dir='rtl'] .btn--solid:hover::before {
  transform: translateX(-115%);
}

.btn--ghost {
  border-color: var(--line);
  color: var(--paper-80);
  background: rgba(255, 255, 255, 0.012);
}
.btn--ghost::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(80% 140% at 50% 130%, rgba(178, 91, 240, 0.45), transparent 70%);
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
.btn--ghost:hover {
  color: var(--paper);
  border-color: rgba(178, 91, 240, 0.55);
}
.btn--ghost:hover::before {
  opacity: 1;
}

/* ══ HEADER — the rail ════════════════════════════════════════════════════
   Weightless over the hero; on scroll it gathers inward and a glass capsule
   settles in behind it. The header is fixed, so the whole morph is contained
   and nothing in the document moves. */

.hdr {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;
  block-size: var(--nav-h);
  display: flex;
  align-items: center;
  pointer-events: none;
  transition: block-size 0.7s var(--ease);
}

.hdr__rail {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(0.8rem, 2vw, 2.2rem);
  inline-size: 100%;
  max-inline-size: var(--shell);
  margin-inline: auto;
  /* Held sideways, an iPhone puts the notch over one end of this rail — the
     brand at one edge, the burger at the other. Clear it. */
  padding-inline:
    max(var(--gutter), env(safe-area-inset-left))
    max(var(--gutter), env(safe-area-inset-right));
  pointer-events: auto;
  transition: max-inline-size 0.8s var(--ease);
}

.hdr__glass {
  position: absolute;
  inset-block: -0.6rem;
  inset-inline: calc(var(--gutter) - 1.1rem);
  z-index: -1;
  border-radius: var(--radius-pill);
  background: rgba(11, 8, 24, 0.72);
  border: 1px solid var(--line);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.02) inset;
  opacity: 0;
  transform: scaleX(0.97);
  transition: opacity 0.55s var(--ease), transform 0.85s var(--ease-orbit);
  pointer-events: none;
}

.hdr.is-condensed {
  block-size: var(--nav-h-min);
}
.hdr.is-condensed .hdr__rail {
  max-inline-size: 1220px;
}
.hdr.is-condensed .hdr__glass,
body:not([data-page='home']) .hdr__glass {
  opacity: 1;
  transform: none;
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
}

/* ── Brand: mark in a socket, ring in orbit ───────────────────────────── */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  flex: none;
}
.brand__socket {
  position: relative;
  inline-size: 2.6rem;
  block-size: 2.6rem;
  display: grid;
  place-items: center;
  flex: none;
  transition: transform 0.7s var(--ease-orbit);
}
.brand:hover .brand__socket {
  transform: scale(1.08);
}
/* Driven by js/cosmos.js: the ring turns on its own and speeds up with
   scroll velocity, so the mark reads as an instrument under way. */
.brand__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    transparent 0deg,
    rgba(178, 91, 240, 0.15) 90deg,
    var(--violet) 200deg,
    var(--rose) 250deg,
    rgba(255, 215, 154, 0.9) 285deg,
    transparent 320deg
  );
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 1.6px), #000 calc(100% - 1.1px));
  mask: radial-gradient(closest-side, transparent calc(100% - 1.6px), #000 calc(100% - 1.1px));
  transform: rotate(var(--ring-rot, 0deg));
  opacity: 0.9;
}
.brand__spark {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 100%;
  inline-size: 3px;
  block-size: 3px;
  margin-block-start: -1.5px;
  margin-inline-start: -2.5px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 9px var(--gold);
  transform-origin: -1.25rem 1.5px;
  transform: rotate(var(--ring-rot, 0deg));
}
/* The spark is placed with a logical inset but orbits around a physical
   origin, so right-to-left has to mirror the origin too — otherwise the two
   compound and the spark circles a point beside the mark instead of the mark. */
html[dir='rtl'] .brand__spark {
  transform-origin: calc(100% + 1.25rem) 1.5px;
}
.brand__mark {
  inline-size: 1.85rem;
  block-size: 1.85rem;
  object-fit: contain;
  filter: drop-shadow(0 0 10px rgba(178, 91, 240, 0.55));
}
.brand__name {
  font-size: 0.98rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.brand__name span {
  color: var(--paper-48);
  font-weight: 400;
}

/* ── Navigation and the comet ─────────────────────────────────────────── */
.nav {
  position: relative;
  margin-inline-start: auto;
}
.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 2.1rem);
}
.nav__link {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.87rem;
  font-weight: 450;
  color: var(--paper-64);
  padding-block: 0.45rem;
  transition: color var(--dur-fast) var(--ease);
}
.nav__idx {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  color: var(--paper-32);
  transform: translateY(-0.55em);
  transition: color var(--dur-fast) var(--ease), transform 0.5s var(--ease-orbit);
}
.nav__link:hover,
.nav__link.is-current {
  color: var(--paper);
}
.nav__link:hover .nav__idx {
  color: var(--violet-bright);
  transform: translateY(-0.75em);
}

/* One light travels between the links instead of four underlines existing at
   once. Geometry is measured in js/cosmos.js and written as --cx / --csx, so
   this behaves identically right-to-left. */
.comet {
  position: absolute;
  left: 0;
  bottom: 0.15rem;
  inline-size: 100px;
  block-size: 2px;
  border-radius: 2px;
  background: var(--aurora);
  transform-origin: 0 50%;
  transform: translate3d(var(--cx, 0px), 0, 0) scaleX(var(--csx, 0.2));
  opacity: var(--co, 0);
  box-shadow: 0 0 12px rgba(178, 91, 240, 0.75);
  transition: transform 0.62s var(--ease-orbit), opacity 0.45s var(--ease);
  pointer-events: none;
  will-change: transform;
}
.comet::after {
  content: '';
  position: absolute;
  inset: -1px 0;
  border-radius: inherit;
  background: inherit;
  filter: blur(4px);
  opacity: 0.7;
}

/* ── Header actions ──────────────────────────────────────────────────── */
.hdr__actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: none;
}

.lang {
  position: relative;
}
.lang__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.8rem;
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--paper-64);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.lang__toggle:hover,
.lang__toggle[aria-expanded='true'] {
  color: var(--paper);
  border-color: rgba(178, 91, 240, 0.5);
  box-shadow: 0 0 22px rgba(178, 91, 240, 0.18);
}
.lang__chev {
  transition: transform var(--dur-fast) var(--ease);
}
.lang__toggle[aria-expanded='true'] .lang__chev {
  transform: rotate(180deg);
}
.lang__menu {
  position: absolute;
  inset-block-start: calc(100% + 0.7rem);
  inset-inline-end: 0;
  min-inline-size: 11rem;
  padding: 0.4rem;
  background: rgba(14, 10, 30, 0.94);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.66), 0 0 0 1px rgba(255, 255, 255, 0.02) inset;
  display: grid;
  gap: 2px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px) scale(0.98);
  transform-origin: 100% 0;
  transition: opacity var(--dur-fast) var(--ease), transform 0.45s var(--ease-orbit),
    visibility var(--dur-fast);
}
html[dir='rtl'] .lang__menu {
  transform-origin: 0 0;
}
.lang.is-open .lang__menu {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.lang__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  inline-size: 100%;
  padding: 0.6rem 0.7rem;
  font-size: 0.86rem;
  text-align: start;
  border-radius: 8px;
  color: var(--paper-64);
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.lang__option:hover {
  background: rgba(178, 91, 240, 0.14);
  color: var(--paper);
}
.lang__option[aria-current='true'] {
  color: var(--paper);
}
.lang__option[aria-current='true']::after {
  content: '✦';
  color: var(--violet);
  font-size: 0.7rem;
  flex: none;
}
.lang__option-code {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  color: var(--paper-muted);
}

/* ── The gate button ─────────────────────────────────────────────────── */
.burger {
  position: relative;
  display: none;
  inline-size: 2.7rem;
  block-size: 2.7rem;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.burger:hover {
  border-color: rgba(178, 91, 240, 0.5);
  box-shadow: 0 0 22px rgba(178, 91, 240, 0.2);
}
.burger__box {
  inline-size: 15px;
  block-size: 9px;
  position: relative;
}
.burger__box span {
  position: absolute;
  inset-inline: 0;
  block-size: 1.5px;
  background: var(--paper);
  border-radius: 2px;
  transition: transform 0.45s var(--ease-orbit), opacity var(--dur-fast) var(--ease);
}
.burger__box span:first-child {
  inset-block-start: 0;
}
.burger__box span:last-child {
  inset-block-end: 0;
}
.is-menu-open .burger__box span:first-child {
  transform: translateY(3.75px) rotate(45deg);
}
.is-menu-open .burger__box span:last-child {
  transform: translateY(-3.75px) rotate(-45deg);
}
.burger__orbit {
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1px dashed rgba(178, 91, 240, 0.55);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  animation: spin 14s linear infinite;
  pointer-events: none;
}
.is-menu-open .burger__orbit {
  opacity: 1;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@keyframes spin-back {
  to {
    transform: rotate(-360deg);
  }
}

/* ══ THE GATE — small-screen navigation ═══════════════════════════════════
   Opens as an aperture from the button that triggered it. The origin is
   written by script as --gx / --gy, so it always grows out of the tap. */

.gate {
  position: fixed;
  inset: 0;
  /* `dvh`, not the `lvh` the sky uses. The sky wants the largest the window
     ever gets, because it is behind everything and must never run out. The
     menu wants whatever the window is right now: sized to the largest, its
     bottom row — the address and the language switcher — would sit below the
     fold whenever the address bar happens to be showing. dvh tracks, and the
     page underneath is not scrolling while this is open, so there is nothing
     for the tracking to fight with. */
  min-block-size: 100dvh;
  z-index: 99;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2rem;
  padding:
    calc(var(--nav-h) + 1.5rem)
    max(var(--gutter), env(safe-area-inset-right))
    max(2.5rem, env(safe-area-inset-bottom))
    max(var(--gutter), env(safe-area-inset-left));
  /* The menu is a fixed panel the height of the window, and its contents —
     three large links, the address, three languages — are taller than that
     on a short phone or any phone held sideways. It used to clip them with
     no way to reach them; now the panel itself scrolls. */
  overflow-y: auto;
  overscroll-behavior: contain;
  visibility: hidden;
  clip-path: circle(0px at var(--gx, 92%) var(--gy, 44px));
  transition: clip-path 0.9s var(--ease-inout), visibility 0s linear 0.9s;
}
.is-menu-open .gate {
  visibility: visible;
  clip-path: circle(150% at var(--gx, 92%) var(--gy, 44px));
  transition: clip-path 0.9s var(--ease-inout), visibility 0s linear 0s;
}
.gate__sky {
  /* Fixed, not absolute: the panel scrolls now, and the sky behind it must
     stay pinned to the window instead of sliding up with the links. */
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(90% 60% at 80% 0%, rgba(78, 36, 160, 0.85), transparent 62%),
    radial-gradient(70% 50% at 10% 100%, rgba(255, 111, 216, 0.16), transparent 66%),
    rgba(6, 4, 16, 0.975);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
}
.gate__mark {
  position: fixed;
  inset-inline-end: -20%;
  inset-block-end: -8%;
  inline-size: 70%;
  max-inline-size: 30rem;
  opacity: 0.14;
  z-index: -1;
  animation: float-slow 18s var(--ease-inout) infinite alternate;
}
@keyframes float-slow {
  from {
    transform: translate3d(0, 0, 0) rotate(-3deg);
  }
  to {
    transform: translate3d(0, -3%, 0) rotate(3deg);
  }
}
.gate__list {
  display: grid;
  gap: 0.15rem;
  align-content: center;
  flex: 1;
}
.gate__link {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  font-family: var(--font-display);
  font-size: clamp(2.1rem, 11vw, 3.4rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  padding-block: 0.3rem;
  color: var(--paper);
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition: opacity 0.7s var(--ease), transform 0.8s var(--ease-orbit),
    color var(--dur-fast) var(--ease);
}
html[lang='ar'] .gate__link {
  font-family: var(--font-ar);
  font-weight: 600;
  letter-spacing: 0;
}
.is-menu-open .gate__link {
  opacity: 1;
  transform: none;
}
.gate__link .nav__idx {
  font-size: 0.62rem;
  transform: none;
  color: var(--violet);
}
.gate__link::after {
  content: '';
  position: absolute;
  inset-block-end: 0.35rem;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 1px;
  background: var(--aurora);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.7s var(--ease);
}
html[dir='rtl'] .gate__link::after {
  transform-origin: right center;
}
.gate__link.is-current::after,
.gate__link:hover::after {
  transform: scaleX(1);
}
.gate__foot {
  display: grid;
  gap: 1.1rem;
  position: relative;
}
.gate__email {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--paper-64);
  letter-spacing: 0.02em;
}
.gate__langs {
  display: flex;
  gap: 0.5rem;
}
.gate__lang {
  padding: 0.5rem 0.95rem;
  font-size: 0.82rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--paper-48);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.gate__lang[aria-current='true'] {
  color: var(--paper);
  border-color: rgba(178, 91, 240, 0.6);
  box-shadow: 0 0 20px rgba(178, 91, 240, 0.18);
}

@media (max-width: 1000px) {
  .nav {
    display: none;
  }
  .burger {
    display: grid;
  }
  .hdr__actions {
    margin-inline-start: auto;
  }
}
@media (min-width: 1001px) {
  .gate {
    display: none;
  }
}
@media (max-width: 400px) {
  .brand__name {
    display: none;
  }
}

/* ══ THE JUMP — moving between pages ══════════════════════════════════════
   Leaving a page is a departure: the star field stretches into streaks, the
   page falls away from the reader, and the void closes over it from the
   middle out. Arriving is the same event in reverse — the void collapses back
   to a point and the new page opens out of it.

   The header rail sits above the veil (z-index 100 against 98) and never
   moves, so the two documents read as one place with the lights changing.

   Everything here is driven by two classes on <html>:
     .is-jumping   the exit, added by js/cosmos.js on an internal link click
     .is-arriving  the entrance, added by the inline script in each document's
                   <head> when the previous page set the jump flag — before
                   first paint, so the new page never flashes in unveiled

   The entrance is a pure CSS animation on purpose. If the module fails to
   load, the veil still opens and the page is still readable. */

.veil {
  position: fixed;
  inset: 0;
  /* Same reason as .sky — it must cover the address-bar strip too. */
  block-size: 100lvh;
  z-index: 98;
  pointer-events: none;
  visibility: hidden;
  contain: strict;
}

/* The void itself. This used to be a full-bleed plane whose `clip-path:
   circle()` radius was animated, which reads well on a desktop and costs a
   full-viewport repaint on every single frame — the reason the jump stuttered
   on a phone. It is now a real circle that is simply scaled, so the whole
   departure runs on the compositor and never repaints at all.

   150vmax is the smallest diameter that still covers the viewport corners at
   scale(1): the diagonal can reach 1.414 × vmax. */
.veil__plane {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: 150vmax;
  block-size: 150vmax;
  margin-block-start: -75vmax;
  margin-inline-start: -75vmax;
  border-radius: 50%;
  transform: scale(0);
  /* The core stop is 36%, not 46%: the element is now larger than the
     viewport, so the violet centre needs pulling in to land where it did. */
  background:
    radial-gradient(circle at 50% 50%, rgba(78, 36, 168, 0.92), rgba(9, 6, 24, 0.99) 36%),
    var(--void);
}

/* Light thrown outward from the point the page collapses into. */
.veil__streaks {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: 220vmax;
  block-size: 220vmax;
  margin-block-start: -110vmax;
  margin-inline-start: -110vmax;
  opacity: 0;
  background: repeating-conic-gradient(
    from 0deg at 50% 50%,
    transparent 0deg 2.1deg,
    rgba(217, 184, 255, 0.18) 2.1deg 2.5deg,
    transparent 2.5deg 4.4deg,
    rgba(255, 111, 216, 0.12) 4.4deg 4.7deg
  );
  -webkit-mask-image: radial-gradient(circle, transparent 8%, #000 34%, transparent 62%);
  mask-image: radial-gradient(circle, transparent 8%, #000 34%, transparent 62%);
}

/* The edge of the closing aperture. */
.veil__rim {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: 150vmax;
  block-size: 150vmax;
  margin-block-start: -75vmax;
  margin-inline-start: -75vmax;
  opacity: 0;
  background: radial-gradient(
    circle,
    transparent 46%,
    rgba(233, 214, 255, 0.55) 49%,
    rgba(178, 91, 240, 0.35) 52%,
    transparent 58%
  );
}

/* ── Departure ─────────────────────────────────────────────────────────── */

.is-jumping {
  pointer-events: none;
}
.is-jumping .veil {
  visibility: visible;
}
/* Hinted only while the jump runs. Held permanently, these three layers pin
   a texture the size of the screen each — tens of megabytes of GPU memory
   reserved for an element that is hidden the entire time anyone is actually
   reading, which on a phone gets paid back as jank somewhere else. Keyframed
   transforms and opacity are promoted automatically when they start, so
   there is nothing lost by scoping it. */
.is-jumping .veil__plane,
.is-jumping .veil__rim,
.is-jumping .veil__streaks,
.is-arriving .veil__plane,
.is-arriving .veil__rim,
.is-arriving .veil__streaks {
  will-change: transform, opacity;
}
.is-jumping .veil__plane {
  animation: veil-close 0.52s var(--ease-inout) forwards;
}
.is-jumping .veil__rim {
  animation: veil-rim-out 0.62s var(--ease-inout) forwards;
}
.is-jumping .veil__streaks {
  animation: veil-streaks-out 0.62s var(--ease-inout) forwards;
}
.is-jumping #main {
  transform: scale(0.965) translate3d(0, -10px, 0);
  opacity: 0;
  transition: transform 0.52s var(--ease-inout), opacity 0.42s var(--ease-inout);
}
.is-jumping .footer {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: transform 0.5s var(--ease-inout), opacity 0.4s var(--ease-inout);
}
/* The gate is above the veil in the stack; on a jump it steps aside at once
   rather than playing its own closing animation over the departure. */
.is-jumping .gate {
  opacity: 0;
  transition: opacity 0.16s linear;
}

/* Text, buttons and chips leave a beat before the plate they sit on. */
.is-jumping .stage__copy > *,
.is-jumping .page-head > *,
.is-jumping .cta__inner > * {
  animation: jump-out 0.4s var(--ease-inout) forwards;
}
.is-jumping .stage__copy > :nth-child(2),
.is-jumping .page-head > :nth-child(2),
.is-jumping .cta__inner > :nth-child(2) {
  animation-delay: 0.035s;
}
.is-jumping .stage__copy > :nth-child(3),
.is-jumping .page-head > :nth-child(3),
.is-jumping .cta__inner > :nth-child(3) {
  animation-delay: 0.07s;
}
.is-jumping .stage__copy > :nth-child(n + 4),
.is-jumping .page-head > :nth-child(n + 4),
.is-jumping .cta__inner > :nth-child(n + 4) {
  animation-delay: 0.105s;
}
.is-jumping .orrery {
  animation: jump-collapse 0.5s var(--ease-inout) forwards;
}

@keyframes veil-close {
  from {
    transform: scale(0);
  }
  to {
    transform: scale(1);
  }
}
@keyframes veil-rim-out {
  0% {
    opacity: 0.9;
    transform: scale(0.04);
  }
  100% {
    opacity: 0;
    transform: scale(1.35);
  }
}
@keyframes veil-streaks-out {
  0% {
    opacity: 0;
    transform: scale(0.16) rotate(0deg);
  }
  22% {
    opacity: 0.85;
  }
  100% {
    opacity: 0;
    transform: scale(1.1) rotate(9deg);
  }
}
@keyframes jump-out {
  to {
    opacity: 0;
    transform: translate3d(0, -22px, 0);
  }
}
@keyframes jump-collapse {
  to {
    opacity: 0;
    transform: scale(0.86) rotate(-8deg);
  }
}

/* ── Arrival ───────────────────────────────────────────────────────────── */

.is-arriving .veil {
  visibility: visible;
}
.is-arriving .veil__plane {
  animation: veil-open 0.62s var(--ease-inout) forwards;
}
.is-arriving .veil__rim {
  animation: veil-rim-in 0.78s var(--ease-inout) forwards;
}
.is-arriving .veil__streaks {
  animation: veil-streaks-in 0.72s var(--ease-inout) forwards;
}
/* The new page is already faded out at the first painted frame, so nothing is
   ever visible through the veil before it opens. */
.is-arriving #main,
.is-arriving .footer {
  animation: arrive 0.85s var(--ease) both;
}
.is-arriving .footer {
  animation-delay: 0.1s;
}
.is-arriving .hdr__rail {
  animation: arrive-rail 0.6s var(--ease) both;
}

@keyframes veil-open {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(0);
  }
}
@keyframes veil-rim-in {
  0% {
    opacity: 0.9;
    transform: scale(1.35);
  }
  100% {
    opacity: 0;
    transform: scale(0.02);
  }
}
@keyframes veil-streaks-in {
  0% {
    opacity: 0.7;
    transform: scale(1.05) rotate(6deg);
  }
  100% {
    opacity: 0;
    transform: scale(0.2) rotate(0deg);
  }
}
@keyframes arrive {
  0% {
    opacity: 0;
    transform: scale(1.035) translate3d(0, 14px, 0);
  }
  28% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: none;
  }
}
@keyframes arrive-rail {
  0% {
    opacity: 0;
    transform: translate3d(0, -12px, 0);
  }
  100% {
    opacity: 1;
    transform: none;
  }
}

/* ── The jump on a phone ──────────────────────────────────────────────────
   Same choreography, cheaper layers. A phone rasterises all of this at two
   or three device pixels per CSS pixel, so what costs is area — and the
   departure only has half a second of frames to spend. */
@media (pointer: coarse) {
  /* 220vmax of repeating-conic-gradient is several million pixels of the
     most expensive gradient there is, and the mask only ever reveals a ring
     of it. Just over half the width is under a third of the area. */
  .veil__streaks {
    inline-size: 120vmax;
    block-size: 120vmax;
    margin-block-start: -60vmax;
    margin-inline-start: -60vmax;
  }
  .veil__rim {
    inline-size: 110vmax;
    block-size: 110vmax;
    margin-block-start: -55vmax;
    margin-inline-start: -55vmax;
  }

  /* The orrery holds two <video> elements. Rotating that layer makes the
     compositor resample decoded video frames on every tick; scaling and
     fading it does not, and under a closing void the rotation was never
     legible anyway. */
  .is-jumping .orrery {
    animation-name: jump-collapse-flat;
  }

  /* Arrival is the half a visitor actually waits through — the departure is
     covered by their own tap. So this is the half worth shortening. */
  .is-arriving #main,
  .is-arriving .footer {
    animation-duration: 0.62s;
  }
  .is-arriving .veil__plane {
    animation-duration: 0.5s;
  }
  .is-arriving .veil__rim {
    animation-duration: 0.6s;
  }
  .is-arriving .veil__streaks {
    animation-duration: 0.56s;
  }

  /* The menu is the way between pages on a phone, so its reveal is part of
     this journey. The panel behind it is already 97.5% opaque, which makes
     the blur under it all but invisible — and a backdrop-filter repainting
     under a growing clip-path is the most expensive thing on the page. */
  .gate__sky {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* The header capsule is fixed, and a backdrop-filter on a fixed element
     means the browser re-samples and re-blurs whatever is passing behind it
     on every single scroll frame. That is the work that starves the
     rasteriser, and starving the rasteriser is what leaves fresh screen
     unpainted while a finger is moving. The capsule is 72% opaque already,
     so taking it to 93% loses very little and costs nothing per frame. */
  .hdr.is-condensed .hdr__glass,
  body:not([data-page='home']) .hdr__glass {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: rgba(11, 8, 24, 0.93);
  }
}

@keyframes jump-collapse-flat {
  to {
    opacity: 0;
    transform: scale(0.86);
  }
}

/* Reduced motion: no departure at all (js/cosmos.js lets the browser navigate
   normally), and an arrival that is only ever a short fade. */
@media (prefers-reduced-motion: reduce) {
  .veil,
  .is-jumping .veil,
  .is-arriving .veil {
    display: none;
  }
  .is-jumping #main,
  .is-jumping .footer,
  .is-jumping .stage__copy > *,
  .is-jumping .page-head > *,
  .is-jumping .cta__inner > *,
  .is-jumping .orrery,
  .is-arriving #main,
  .is-arriving .footer,
  .is-arriving .hdr__rail {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ══ THE STAGE — hero ═════════════════════════════════════════════════════ */

.stage {
  position: relative;
  min-block-size: 100svh;
  display: flex;
  align-items: center;
  padding-block: calc(var(--nav-h) + clamp(1rem, 3svh, 2.75rem)) clamp(2.5rem, 5svh, 4rem);
  isolation: isolate;
  /* The halo is a glow drawn 18% larger than the orrery on every side, which
     on a phone puts it about 40px past both edges of the screen. The root
     clips it so it was never *visible* — but it still widened the document,
     and a document wider than the window is what lets a thumb drag the page
     sideways. Clipping here costs nothing (the stage is full-bleed, so the
     glow still spreads the whole width) and removes the overflow itself. */
  overflow-x: clip;
}

.stage__inner {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: clamp(1rem, 2.6vh, 2.4rem);
  inline-size: 100%;
  text-align: center;
}

.stage__copy {
  display: grid;
  gap: clamp(1rem, 1.5vw, 1.5rem);
  max-inline-size: 38rem;
}

.stage__title {
  line-height: 0.94;
}
html[lang='ar'] .stage__title {
  line-height: 1.2;
}

.stage__disciplines {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55rem;
  padding-block-start: 0.2rem;
}
.stage__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.42rem 0.85rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--paper-muted);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.02);
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease),
    transform 0.5s var(--ease-orbit);
}
.stage__chip::before {
  content: '';
  inline-size: 4px;
  block-size: 4px;
  border-radius: 50%;
  background: var(--violet);
  box-shadow: 0 0 8px var(--violet);
  flex: none;
}
.stage__chip:hover {
  color: var(--paper);
  border-color: rgba(178, 91, 240, 0.45);
  transform: translateY(-2px);
}
html[lang='ar'] .stage__chip {
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.8rem;
}

.stage__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding-block-start: 0.4rem;
}

/* Descent cue — a spark falling down a hairline. */
.descend {
  position: absolute;
  inset-block-end: clamp(1.1rem, 2.6vw, 2rem);
  inset-inline-start: var(--gutter);
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.64rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--paper-32);
  z-index: 3;
}
html[lang='ar'] .descend {
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.78rem;
}
.descend i {
  position: relative;
  display: block;
  inline-size: 1px;
  block-size: 40px;
  overflow: hidden;
  background: linear-gradient(180deg, var(--paper-16), transparent);
}
.descend i::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  block-size: 14px;
  background: linear-gradient(180deg, transparent, var(--violet-bright));
  animation: descend-cue 2.6s var(--ease-inout) infinite;
}
@keyframes descend-cue {
  0% {
    transform: translateY(-16px);
    opacity: 0;
  }
  35% {
    opacity: 1;
  }
  100% {
    transform: translateY(40px);
    opacity: 0;
  }
}

/* ══ THE ORRERY ═══════════════════════════════════════════════════════════
   A working instrument: two dials turning at different rates, four scene
   nodes in slow orbit, and an aperture in the middle that the films play
   inside. Scale is one variable — --ap, the aperture diameter. */

.orrery {
  /* As large as the screen allows. 32rem is the ceiling because the films are
     1024×576 and `cover` starts upscaling past it; on a short window the
     height left over on the stage is what decides instead. */
  --ap: clamp(13rem, min(46vw, 100svh - 27rem), 32rem);
  --orbit: calc(var(--ap) * 0.5 + clamp(1.7rem, 3.4vw, 3.1rem));
  position: relative;
  inline-size: calc(var(--orbit) * 2);
  block-size: calc(var(--orbit) * 2);
  display: grid;
  place-items: center;
  flex: none;
  --p: 0.5;
  /* --p is the scroll-linked drift written by js/motion.js; --ox/--oy are the
     pointer lean written by js/cosmos.js. One transform, two inputs. */
  transform: translate3d(var(--ox, 0px), calc((0.5 - var(--p)) * 60px + var(--oy, 0px)), 0);
}

.orrery__halo {
  position: absolute;
  inset: -18%;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(178, 91, 240, 0.3),
    rgba(109, 58, 212, 0.12) 42%,
    transparent 68%
  );
  animation: breathe 9s var(--ease-inout) infinite alternate;
  pointer-events: none;
}
@keyframes breathe {
  from {
    transform: scale(0.94);
    opacity: 0.75;
  }
  to {
    transform: scale(1.06);
    opacity: 1;
  }
}

.orrery__dial {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  pointer-events: none;
}
.orrery__dial--slow {
  animation: spin 150s linear infinite;
}
.orrery__dial--fast {
  animation: spin-back 90s linear infinite;
}
.orrery__ticks {
  fill: none;
  stroke: rgba(217, 184, 255, 0.34);
  stroke-width: 7;
  stroke-dasharray: 1.6 18.3;
}
.orrery__hoop {
  fill: none;
  stroke: rgba(170, 140, 255, 0.22);
  stroke-width: 1;
}
.orrery__hoop--dashed {
  stroke-dasharray: 3 9;
  stroke: rgba(255, 111, 216, 0.28);
}
.orrery__arc {
  fill: none;
  stroke: url(#orrery-arc);
  stroke-width: 2;
  stroke-linecap: round;
}

/* Saturn-like ring system around the film. The far half sits behind the
   aperture while the near half crosses its face, so the video reads as a
   three-dimensional planet instead of a flat circular frame.

   Three things do the convincing, and they are worth naming because the
   first draft of this had none of them and read as a striped ellipse:

     1. Structure. Not evenly repeating stripes — a faint inner haze, one
        bright dense band, a dark division cut through it, then a thinner
        outer band fading to nothing. Real ring systems are mostly gap.
     2. One light. The earlier version swept the whole aurora around the
        ellipse, which made a rainbow hoop. Here the sweep runs violet up
        to a single ivory-gold arc — one source, upper left, the same one
        the bezel and the disc are lit from.
     3. The planet's shadow, thrown across the rings on the side away from
        that light. It is the detail the eye reads as "this body is round
        and something is behind it", and nothing else sells the illusion
        half as well. */
.orrery__rings {
  position: absolute;
  left: 50%;
  top: 50%;
  inline-size: min(calc(var(--ap) * 1.58), calc(100vw - 1.25rem));
  aspect-ratio: 3.55 / 1;
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%) rotate(-11deg);
  background:
    /* Band structure. Percentages run along the gradient ray, so these are
       ring radii: inner haze, B ring, the division, A ring, outer dust.
       Nothing may sit past 70.7% — that is where the element's own ellipse
       cuts the paint, and a stop beyond it ends the ring on a hard edge
       instead of a fade. Every stop below stays inside that limit. */
    radial-gradient(
      ellipse at center,
      transparent 0 47%,
      rgba(233, 226, 255, 0.13) 48.5%,
      rgba(233, 226, 255, 0.05) 49.9%,
      rgba(255, 240, 214, 0.36) 51.6%,
      rgba(255, 255, 255, 0.46) 53.8%,
      rgba(238, 226, 255, 0.28) 55.8%,
      rgba(255, 255, 255, 0.05) 57.5%,
      rgba(255, 255, 255, 0.04) 58.8%,
      rgba(255, 215, 154, 0.33) 60.2%,
      rgba(233, 214, 255, 0.26) 62.5%,
      rgba(255, 255, 255, 0.12) 64.9%,
      rgba(255, 255, 255, 0.05) 67.2%,
      transparent 70%
    ),
    /* The light. One warm arc, violet everywhere else. */
    conic-gradient(
      from 198deg,
      rgba(109, 58, 212, 0.5),
      rgba(178, 91, 240, 0.56) 12%,
      rgba(217, 184, 255, 0.8) 26%,
      rgba(255, 240, 214, 0.9) 40%,
      rgba(255, 215, 154, 0.6) 52%,
      rgba(178, 91, 240, 0.4) 70%,
      rgba(109, 58, 212, 0.5) 88%,
      rgba(109, 58, 212, 0.5)
    );
  /* The band shape lives in a variable because each half has to composite
     it with its own fade across the midline — see the two rules below. */
  --ring-band: radial-gradient(
    ellipse at center,
    transparent 0 46%,
    rgba(0, 0, 0, 0.22) 48%,
    rgba(0, 0, 0, 0.85) 51%,
    #000 53.5%,
    #000 63%,
    rgba(0, 0, 0, 0.72) 66%,
    rgba(0, 0, 0, 0.28) 68.5%,
    transparent 70.2%
  );
  -webkit-mask: var(--ring-band);
  mask: var(--ring-band);
}

/* The shadow the planet throws across its own rings, opposite the light.
   A child, so the ring's mask and its near/far clip apply to it unchanged
   — the shadow can only ever fall on ring material, never on empty sky. */
.orrery__rings::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    40% 128% at 67% 50%,
    rgba(4, 3, 12, 0.92) 0 18%,
    rgba(4, 3, 12, 0.55) 42%,
    rgba(4, 3, 12, 0.12) 62%,
    transparent 74%
  );
}

/* Splitting one ellipse into a far half and a near half is only a z-order
   trick — at the left and right tips the ring is one continuous arm, and
   there is nothing there to see a join in. The first version cut it with
   clip-path on a straight line at the midline and then gave each side its
   own opacity and its own drop-shadow, so the cut edge was straight, hard,
   and traced by a 2px shadow: a visible line ruled across both arms.

   Now each half fades out over the midline while the other fades in beneath
   it, the opacities sit close enough that the crossfade has nothing to step
   between, and no filter is tight enough to draw an edge. */
.orrery__rings--far {
  z-index: 1;
  /* Draws the ring whole, not just its top half. Behind the disc, so the
     middle is hidden anyway and only the two tips show — but because it is
     one continuous band with no cut in it, there is no join at those tips
     for the eye to find. This is the layer that removes the line; the near
     arm below simply fades in on top of it. */
  opacity: 0.82;
  filter: drop-shadow(0 -3px 11px rgba(109, 58, 212, 0.22));
}
.orrery__rings--near {
  z-index: 4;
  /* Just short of opaque: ring material is thin, and a hint of the disc
     showing through where the near arc crosses it is what keeps this from
     looking like a hoop painted on the front. */
  opacity: 0.96;
  /* Fades in across the midline instead of starting on a cut edge. The
     band mask and this fade are intersected, so the arm is still ring
     material — just ring material that arrives gradually. */
  -webkit-mask: var(--ring-band), linear-gradient(to top, #000 0 42%, transparent 58%);
  -webkit-mask-composite: source-in;
  mask: var(--ring-band), linear-gradient(to top, #000 0 42%, transparent 58%);
  mask-composite: intersect;
  /* One broad glow only. The old second shadow was 2px — short enough to
     render as a hard warm line along whatever edge it found. */
  filter: drop-shadow(0 5px 12px rgba(178, 91, 240, 0.34));
}

/* The material the pointer knocks loose out of the band, drawn by
   js/cosmos.js. Above the near arm so a splash crosses in front of the ring
   it came from, below the read-out so it can never obscure a label. The
   element is created and sized in script; only its place in the stack
   belongs here.

   Deliberately no mix-blend-mode. Screen blend would read slightly better
   over a bright frame, but it makes the whole orrery — the playing film
   included — a blend group the compositor has to resolve every frame, and
   the grains are drawn additively on the canvas anyway. */
.orrery__dust {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  pointer-events: none;
}

/* The aperture itself. */
.aperture {
  position: relative;
  inline-size: var(--ap);
  block-size: var(--ap);
  border-radius: 50%;
  overflow: hidden;
  background: #07061a;
  box-shadow: 0 0 0 1px rgba(217, 184, 255, 0.18), 0 34px 90px rgba(0, 0, 0, 0.62),
    0 0 90px rgba(140, 80, 255, 0.16);
  isolation: isolate;
  z-index: 2;
}
.aperture__layer {
  position: absolute;
  inset: 0;
  clip-path: circle(0% at 50% 50%);
  transition: clip-path 1.05s var(--ease-orbit);
  will-change: clip-path;
}
.aperture__layer.is-current {
  clip-path: circle(62% at 50% 50%);
}
.aperture__layer video {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transform: scale(1.04);
  transition: transform 6s linear;
}
.aperture__layer.is-current video {
  transform: scale(1.11);
}

/* The final SIGNAL//ZERO reward is a free-standing mark, not a picture inside
   the film disc. It is a sibling of the aperture so the circular clip and
   glass edge can disappear completely while the Saturn ring still splits
   naturally behind (z1) and in front of it (z4). */
.orrery__emblem {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  inline-size: min(calc(var(--ap) * 1.48), calc(100vw - 0.75rem));
  aspect-ratio: 1024 / 820;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -50%) scale(0.9);
  pointer-events: none;
  transition:
    opacity 0.9s var(--ease),
    transform 1.2s var(--ease-orbit),
    visibility 0s linear 1.2s;
}
.orrery__emblem img {
  inline-size: 100%;
  block-size: auto;
  max-inline-size: none;
  mix-blend-mode: screen;
  filter: saturate(1.1) contrast(1.04) drop-shadow(0 0 26px rgba(178, 91, 240, 0.52));
}

html.signal-zero-complete .orrery .aperture,
.orrery.is-signal-complete .aperture {
  opacity: 0 !important;
  visibility: hidden;
  pointer-events: none;
}
html.signal-zero-complete .orrery .orrery__emblem,
.orrery.is-signal-complete .orrery__emblem {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(1);
  transition-delay: 0s;
}

/* The reference composition uses a broad, almost horizon-wide Saturn sweep.
   Enlarge and flatten only the completed version; the film planet keeps its
   original, tighter ring proportions before the game is finished. */
html.signal-zero-complete .orrery .orrery__rings,
.orrery.is-signal-complete .orrery__rings {
  inline-size: min(calc(var(--ap) * 2.65), calc(100vw - 0.25rem));
  aspect-ratio: 5.4 / 1;
}

/* Film controls and every circular piece of the old aperture leave together.
   Only the elliptical Saturn ring system and its dust remain around the mark. */
html.signal-zero-complete .orrery .orrery__nodes,
html.signal-zero-complete .orrery .orrery__read,
html.signal-zero-complete .orrery .orrery__meter,
html.signal-zero-complete .orrery .orrery__dial,
html.signal-zero-complete .orrery .aperture__bezel,
.orrery.is-signal-complete .orrery__nodes,
.orrery.is-signal-complete .orrery__read,
.orrery.is-signal-complete .orrery__meter,
.orrery.is-signal-complete .orrery__dial,
.orrery.is-signal-complete .aperture__bezel {
  display: none;
}
/* Grade and vignette live in one static overlay, so the video itself is
   never filtered per frame. */
.aperture__veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 50%, transparent 42%, rgba(5, 4, 14, 0.55) 78%, rgba(5, 4, 14, 0.9) 100%),
    linear-gradient(160deg, rgba(109, 58, 212, 0.34), transparent 45%, rgba(255, 111, 216, 0.18));
  mix-blend-mode: soft-light;
}
.aperture__veil::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Films vary from a night desk to a white UI kit. This keeps the brightest
     frames from out-shouting the headline without muddying the dark ones. */
  background: radial-gradient(circle at 50% 50%, rgba(6, 4, 18, 0.2) 28%, rgba(5, 4, 14, 0.7) 88%);
  mix-blend-mode: normal;
}
/* ── The rim ──────────────────────────────────────────────────────────────
   Two coats of light on the inside of the disc. Together they stop the film
   from reading as a hole punched in the page: one gives the glass an edge,
   the other lights the body like a planet with a source somewhere off to the
   upper left — the same direction the ring system is lit from. */

/* Atmosphere. Screen blend, so it can only add light, never darken a frame. */
.aperture::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(78% 78% at 24% 16%, rgba(255, 215, 154, 0.2), transparent 58%),
    radial-gradient(60% 60% at 82% 88%, rgba(255, 111, 216, 0.14), transparent 62%);
  mix-blend-mode: screen;
}

/* Glass edge, and the shadow that seats the disc in its bezel. Normal blend
   — this one has to be allowed to darken. */
.aperture::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 6;
  border-radius: 50%;
  pointer-events: none;
  box-shadow:
    inset 0 0 0 1px rgba(233, 214, 255, 0.34),
    inset 0 1.5px 0 rgba(255, 255, 255, 0.16),
    inset 0 -1.5px 0 rgba(109, 58, 212, 0.4),
    inset 0 -22px 38px -26px rgba(4, 3, 12, 0.95),
    inset 0 16px 30px -24px rgba(217, 184, 255, 0.4);
}

.aperture__flare {
  position: absolute;
  inset: 0;
  z-index: 4;
  background: radial-gradient(circle at 50% 50%, rgba(233, 214, 255, 0.85), transparent 62%);
  opacity: 0;
  pointer-events: none;
}

/* ── The bezel ────────────────────────────────────────────────────────────
   A machined ring the disc sits in, cut from the aurora and masked to a
   band. It turns once every four minutes, which is what moves the specular
   highlight around the rim — the only thing animating here is a transform.
   It sits above the film (so it caps the edge) and below the nodes and the
   near ring, which still pass in front of everything. */
.aperture__bezel {
  /* The band scales with the disc, or an 8px ring that looks machined at
     468px reads as a thick tyre at 240px. It straddles the rim: a little
     over the film, a little past it. */
  --band: clamp(5px, calc(var(--ap) * 0.019), 9px);
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: calc(var(--ap) + var(--band) * 0.9);
  block-size: calc(var(--ap) + var(--band) * 0.9);
  border-radius: 50%;
  z-index: 2;
  pointer-events: none;
  background: conic-gradient(
    from 205deg,
    rgba(109, 58, 212, 0.5),
    rgba(178, 91, 240, 0.92) 12%,
    rgba(255, 111, 216, 0.6) 26%,
    rgba(255, 215, 154, 1) 41%,
    rgba(239, 224, 255, 0.5) 53%,
    rgba(109, 58, 212, 0.42) 71%,
    rgba(217, 184, 255, 0.88) 88%,
    rgba(109, 58, 212, 0.5)
  );
  -webkit-mask: radial-gradient(
    closest-side,
    transparent calc(100% - var(--band)),
    rgba(0, 0, 0, 0.45) calc(100% - var(--band) + 1.5px),
    #000 calc(100% - var(--band) + 3px),
    #000 calc(100% - 1.5px),
    transparent 100%
  );
  mask: radial-gradient(
    closest-side,
    transparent calc(100% - var(--band)),
    rgba(0, 0, 0, 0.45) calc(100% - var(--band) + 1.5px),
    #000 calc(100% - var(--band) + 3px),
    #000 calc(100% - 1.5px),
    transparent 100%
  );
  filter: drop-shadow(0 2px 10px rgba(178, 91, 240, 0.34));
  animation: spin-back 240s linear infinite;
}
.aperture.is-switching .aperture__flare {
  animation: flare 1s var(--ease) 1;
}
@keyframes flare {
  0% {
    opacity: 0.55;
    transform: scale(0.6);
  }
  100% {
    opacity: 0;
    transform: scale(1.1);
  }
}

.orrery__shock {
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: var(--ap);
  block-size: var(--ap);
  border-radius: 50%;
  border: 1px solid rgba(217, 184, 255, 0.75);
  opacity: 0;
  pointer-events: none;
}
.orrery.is-switching .orrery__shock {
  animation: shock 1.05s var(--ease) 1;
}
@keyframes shock {
  0% {
    opacity: 0.85;
    transform: scale(0.42);
  }
  100% {
    opacity: 0;
    transform: scale(1.32);
  }
}

/* Playback of the current scene, drawn on the aperture rim. */
.orrery__meter {
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: calc(var(--ap) + 1.1rem);
  block-size: calc(var(--ap) + 1.1rem);
  border-radius: 50%;
  background: conic-gradient(
    from -90deg,
    var(--violet-bright) calc(var(--play, 0) * 360deg),
    rgba(217, 184, 255, 0.14) 0
  );
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2px));
  mask: radial-gradient(closest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2px));
  pointer-events: none;
}

/* The head of the playback arc: a light travelling the rim, on the same
   radius as the meter. --play is written on this element by js/cosmos.js, so
   the pseudo-element inherits it and rides the arc on a transform alone. */
.orrery__meter::after {
  content: '';
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  inline-size: 4px;
  block-size: 4px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 7px 1.5px rgba(255, 215, 154, 0.65), 0 0 16px rgba(255, 111, 216, 0.4);
  transform: translate(-50%, -50%) rotate(calc(var(--play, 0) * 360deg))
    translateY(calc((var(--ap) + 1.1rem) / -2 + 1px));
}

/* Scene nodes: the ring turns, each node counter-turns so it stays upright. */
.orrery__nodes {
  position: absolute;
  inset: 0;
  /* Above the aperture (2), below the near ring (4) — the ring still passes
     across the front of the disc, but the nodes are no longer underneath it.
     Without this the film layer, whose clip circle reaches past the disc
     itself, was taking the taps meant for the four dots: on a mouse you
     could still land on one, on a thumb you mostly could not. */
  z-index: 3;
  animation: spin 150s linear infinite;
}
.orrery__node {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) rotate(var(--a)) translateY(calc(var(--orbit) * -1));
}
.orrery__dot {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: 2.35rem;
  block-size: 2.35rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(9, 6, 22, 0.82);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.02em;
  color: var(--paper-48);
  transform: rotate(calc(var(--a) * -1));
  animation: spin-back 150s linear infinite;
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease),
    box-shadow 0.5s var(--ease), background-color 0.4s var(--ease);
}
.orrery__dot::after {
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 1px solid rgba(217, 184, 255, 0.5);
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 0.45s var(--ease), transform 0.55s var(--ease-orbit);
}
.orrery__dot:hover {
  color: var(--paper);
  border-color: rgba(178, 91, 240, 0.6);
  box-shadow: 0 0 26px rgba(178, 91, 240, 0.3);
}
.orrery__dot[aria-pressed='true'] {
  color: #0a0616;
  border-color: transparent;
  background: var(--aurora);
  box-shadow: 0 0 30px rgba(178, 91, 240, 0.55);
}
.orrery__dot[aria-pressed='true']::after {
  opacity: 1;
  transform: scale(1);
}
/* Hovering the instrument stops the orbit so a node is easy to hit. */
.orrery:hover .orrery__nodes,
.orrery:hover .orrery__dot,
.orrery:focus-within .orrery__nodes,
.orrery:focus-within .orrery__dot {
  animation-play-state: paused;
}

/* Read-out under the aperture. */
.orrery__read {
  position: absolute;
  inset-block-end: calc(var(--orbit) - var(--ap) * 0.5 - 0.35rem);
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.35rem 0.4rem 0.35rem 0.95rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: rgba(9, 6, 22, 0.86);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  white-space: nowrap;
  max-inline-size: calc(var(--orbit) * 2 - 1rem);
  z-index: 6;
}
html[dir='rtl'] .orrery__read {
  padding: 0.35rem 0.95rem 0.35rem 0.4rem;
}
.orrery__num {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  color: var(--violet-bright);
  letter-spacing: 0.1em;
}
.orrery__caption {
  font-size: 0.78rem;
  color: var(--paper-80);
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity 0.35s var(--ease), transform 0.45s var(--ease-orbit);
}
.orrery__read.is-changing .orrery__caption {
  opacity: 0;
  transform: translateY(6px);
}
/* Hovering a node previews its film's name; the border says "not yet playing". */
.orrery__read.is-peek {
  border-color: rgba(255, 215, 154, 0.45);
}
.orrery__read.is-peek .orrery__num {
  color: var(--gold);
}

.orrery__sound {
  display: grid;
  place-items: center;
  inline-size: 2rem;
  block-size: 2rem;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--paper-64);
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.orrery__sound:hover {
  border-color: rgba(178, 91, 240, 0.55);
  background: rgba(178, 91, 240, 0.14);
}
/* Three bars: flat when the reel is silent, dancing when it is not. */
.orrery__wave {
  display: flex;
  align-items: center;
  gap: 2px;
  block-size: 11px;
}
.orrery__wave i {
  inline-size: 2px;
  block-size: 11px;
  border-radius: 1px;
  background: var(--paper-32);
  transform: scaleY(0.25);
  transition: background-color 0.35s var(--ease), transform 0.35s var(--ease);
}
.orrery__sound[aria-pressed='true'] .orrery__wave i {
  background: var(--violet-bright);
  animation: equalise 0.9s var(--ease-inout) infinite alternate;
}
.orrery__sound[aria-pressed='true'] .orrery__wave i:nth-child(2) {
  animation-delay: 0.18s;
}
.orrery__sound[aria-pressed='true'] .orrery__wave i:nth-child(3) {
  animation-delay: 0.36s;
}
@keyframes equalise {
  from {
    transform: scaleY(0.28);
  }
  to {
    transform: scaleY(1);
  }
}

/* ══ TRANSMISSION — the claim between hero and work ═══════════════════════ */

.transmission {
  position: relative;
  padding-block: clamp(2.5rem, 5vw, 4rem);
  border-block: 1px solid var(--line-soft);
  overflow: hidden;
}
.transmission__inner {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
}
.transmission__wire {
  position: relative;
  flex: 1;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, rgba(178, 91, 240, 0.4), transparent);
  overflow: hidden;
}
.transmission__wire::after {
  content: '';
  position: absolute;
  inset-block: -1px;
  /* Anchored to the inline start. Without this the pulse falls back to its
     static position, which sits at the far end in Arabic — the light then
     appears halfway along the wire instead of entering from the edge. */
  inset-inline-start: 0;
  inline-size: 22%;
  background: linear-gradient(90deg, transparent, var(--violet-bright), transparent);
  transform: translateX(-120%);
  animation: pulse-wire 5.5s var(--ease-inout) infinite;
}
/* translateX is physical, so right-to-left needs the mirrored travel: the
   pulse enters from the right and runs left, the way the line is read. */
html[dir='rtl'] .transmission__wire::after {
  transform: translateX(120%);
  animation-name: pulse-wire-rtl;
}
.transmission__wire--b::after {
  animation-delay: 2.75s;
}
@keyframes pulse-wire {
  0% {
    transform: translateX(-120%);
  }
  55%,
  100% {
    transform: translateX(560%);
  }
}
@keyframes pulse-wire-rtl {
  0% {
    transform: translateX(120%);
  }
  55%,
  100% {
    transform: translateX(-560%);
  }
}
/* On the stage the strip loses the band it used to sit in and the claim is
   set large: it is the only sentence on the first screen. */
.transmission--stage {
  inline-size: 100%;
  padding-block: 0;
  border-block: 0;
  overflow: visible;
}
.transmission--stage .transmission__inner {
  gap: clamp(0.9rem, 2.6vw, 2.2rem);
}
.transmission--stage .transmission__claim {
  font-size: clamp(1.65rem, 4.1vw, 3.3rem);
  line-height: 1.08;
  /* Wide enough for the longest locale (German, 41.4rem set) to stay on the
     one line the wires flank. */
  max-inline-size: min(92vw, 44rem);
}
html[lang='ar'] .transmission--stage .transmission__claim {
  line-height: 1.2;
}

.transmission__claim {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.6vw, 2.3rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-align: center;
  color: var(--paper);
  flex: none;
  max-inline-size: min(90vw, 34rem);
}
html[lang='ar'] .transmission__claim {
  font-family: var(--font-ar);
  font-weight: 600;
  letter-spacing: 0;
}
.transmission__claim em {
  font-style: italic;
  background: var(--aurora);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* ══ CONSTELLATION — the divider between sections ═════════════════════════
   A small star chart that draws itself as it arrives, with one spark walking
   the line afterwards. The path in `offset-path` must match the path in the
   markup; both are fixed, so they cannot drift apart at runtime. */

.constellation {
  display: block;
  inline-size: 100%;
  max-inline-size: 34rem;
  margin-inline: auto;
  block-size: 60px;
  overflow: visible;
}
.constellation__line {
  fill: none;
  stroke: rgba(178, 91, 240, 0.62);
  stroke-width: 1;
  stroke-dasharray: 700;
  stroke-dashoffset: 700;
  transition: stroke-dashoffset 1.6s var(--ease-inout);
}
.js [data-reveal].is-in .constellation__line {
  stroke-dashoffset: 0;
}
.constellation__star {
  fill: var(--paper);
  opacity: 0;
  transform: scale(0.2);
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity 0.7s var(--ease), transform 0.9s var(--ease-orbit);
}
.js [data-reveal].is-in .constellation__star {
  opacity: 1;
  transform: scale(1);
}
.constellation__star--lit {
  fill: var(--violet-bright);
  filter: drop-shadow(0 0 6px rgba(178, 91, 240, 0.9));
}

.constellation__spark {
  display: none;
}
@supports (offset-path: path('M0 0 L1 1')) {
  .constellation__spark {
    display: block;
    fill: var(--gold);
    filter: drop-shadow(0 0 6px var(--gold));
    offset-path: path('M22 40 L146 17 L268 43 L362 13 L470 37 L578 20');
    offset-rotate: 0deg;
    animation: walk-line 9s var(--ease-inout) infinite;
    opacity: 0;
  }
  .js [data-reveal].is-in .constellation__spark {
    opacity: 1;
  }
}
@keyframes walk-line {
  0% {
    offset-distance: 0%;
    opacity: 0;
  }
  8%,
  86% {
    opacity: 1;
  }
  100% {
    offset-distance: 100%;
    opacity: 0;
  }
}

/* ══ SECTION FRAMING ══════════════════════════════════════════════════════ */

.section-head .lead {
  color: var(--paper-64);
}

/* A faint index number set behind each section title. */
.section--marked {
  position: relative;
}
.section__index {
  position: absolute;
  inset-block-start: clamp(1.5rem, 4vw, 3.5rem);
  inset-inline-end: var(--gutter);
  font-family: var(--font-mono);
  font-size: clamp(3rem, 9vw, 8rem);
  line-height: 1;
  letter-spacing: -0.04em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(170, 140, 255, 0.13);
  pointer-events: none;
  user-select: none;
  z-index: -1;
}

/* ══ CTA — the beacon ═════════════════════════════════════════════════════ */

.cta {
  position: relative;
  overflow: hidden;
}
.cta__ambient {
  background:
    radial-gradient(60% 70% at 50% 118%, rgba(178, 91, 240, 0.34), transparent 66%),
    radial-gradient(40% 50% at 50% 130%, rgba(255, 111, 216, 0.24), transparent 70%);
}
.cta__rings {
  position: absolute;
  inset-block-start: 50%;
  /* The beacon stays physically centred in both writing directions. Using
     inline-start here changes the anchor to `right` in RTL, while the
     physical translate below still moves left and offsets the whole ring. */
  left: 50%;
  inline-size: min(46rem, 92vw);
  block-size: min(46rem, 92vw);
  transform: translate(-50%, -50%);
  z-index: -1;
  pointer-events: none;
  opacity: 0.55;
}
.cta__rings circle {
  fill: none;
  stroke: rgba(170, 140, 255, 0.16);
}
.cta__rings .cta__ring--dashed {
  stroke: rgba(255, 111, 216, 0.2);
  stroke-dasharray: 4 12;
  transform-origin: center;
  animation: spin 120s linear infinite;
}

/* ══ FOOTER — the horizon ═════════════════════════════════════════════════
   The page ends on the rim of a planet: an oversized disc rising from the
   bottom edge with a lit rim, an aurora curtain above it, and the site links
   drawn as constellations. */

.footer {
  position: relative;
  border: 0;
  overflow: hidden;
  padding-block: clamp(6rem, 12vw, 11rem) clamp(1.5rem, 2.5vw, 2rem);
  isolation: isolate;
}

.horizon {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
/* The planet is an ellipse, not a circle: a wide arc with a lit rim crossing
   the footer, dark and near-opaque below it so the page has a floor. The
   aurora is drawn after it in the markup so the curtain hangs above the
   rim rather than behind the body of the planet. */
.horizon__planet {
  position: absolute;
  inset-inline: -30%;
  inset-block-end: -62%;
  block-size: 132%;
  border-radius: 50% 50% 0 0 / 42% 42% 0 0;
  background: linear-gradient(
    180deg,
    rgba(178, 91, 240, 0.26) 0%,
    rgba(28, 14, 62, 0.7) 14%,
    rgba(9, 5, 22, 0.95) 40%,
    rgba(5, 4, 14, 0.99) 100%
  );
  border-block-start: 1px solid rgba(233, 214, 255, 0.55);
  box-shadow: 0 -24px 80px rgba(178, 91, 240, 0.28);
  animation: breathe-planet 22s var(--ease-inout) infinite alternate;
}
@keyframes breathe-planet {
  from {
    transform: translate3d(0, 0, 0) scaleX(1);
  }
  to {
    transform: translate3d(0, -1.2%, 0) scaleX(1.03);
  }
}
/* A curtain of light standing on the rim. */
.horizon__aurora {
  position: absolute;
  inset-inline: -15%;
  inset-block-end: 38%;
  block-size: 26vh;
  max-block-size: 17rem;
  background: linear-gradient(
    180deg,
    transparent,
    rgba(178, 91, 240, 0.26) 44%,
    rgba(255, 111, 216, 0.2) 74%,
    transparent
  );
  filter: blur(46px);
  -webkit-mask-image: repeating-linear-gradient(102deg, #000 0 8%, transparent 8% 27%);
  mask-image: repeating-linear-gradient(102deg, #000 0 8%, transparent 8% 27%);
  animation: aurora-drift 26s var(--ease-inout) infinite alternate;
  will-change: transform;
}
@keyframes aurora-drift {
  from {
    transform: translate3d(-3%, 6%, 0) skewX(-5deg) scaleY(0.92);
    opacity: 0.65;
  }
  to {
    transform: translate3d(4%, -2%, 0) skewX(6deg) scaleY(1.08);
    opacity: 1;
  }
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: clamp(2rem, 4vw, 3.5rem);
  padding-block-end: clamp(2.5rem, 5vw, 4.5rem);
}
.footer__brand {
  display: grid;
  gap: 1.15rem;
  align-content: start;
  max-inline-size: 24rem;
}
/* The mark rises over the horizon like a small moon. */
.footer__moon {
  position: relative;
  inline-size: 4.6rem;
  block-size: 4.6rem;
  display: grid;
  place-items: center;
}
.footer__moon::before {
  content: '';
  position: absolute;
  inset: -28%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(178, 91, 240, 0.32), transparent 66%);
  animation: breathe 8s var(--ease-inout) infinite alternate;
}
.footer__moon::after {
  content: '';
  position: absolute;
  inset: -14%;
  border-radius: 50%;
  border: 1px dashed rgba(170, 140, 255, 0.22);
  animation: spin 90s linear infinite;
}
.footer__moon img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  filter: drop-shadow(0 0 16px rgba(178, 91, 240, 0.5));
}
.footer__tagline {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1.8vw, 1.6rem);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--paper-80);
  max-inline-size: 22rem;
}
html[lang='ar'] .footer__tagline {
  font-family: var(--font-ar);
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.6;
}

.footer__col-title {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper-muted);
  margin-block-end: 1.2rem;
}
html[lang='ar'] .footer__col-title {
  font-family: var(--font-ar);
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.78rem;
}

/* Each column is a small constellation: dots on a line that lights up. */
.footer__list {
  position: relative;
  display: grid;
  gap: 0.62rem;
  padding-inline-start: 0.95rem;
}
.footer__list::before {
  content: '';
  position: absolute;
  inset-block: 0.5rem 0.5rem;
  inset-inline-start: 2px;
  inline-size: 1px;
  background: linear-gradient(180deg, rgba(178, 91, 240, 0.5), rgba(178, 91, 240, 0));
}
.footer__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.9rem;
  color: var(--paper-48);
  inline-size: fit-content;
  transition: color var(--dur-fast) var(--ease), transform 0.45s var(--ease-orbit);
}
.footer__link::before {
  content: '';
  position: absolute;
  inset-inline-start: -0.95rem;
  inset-block-start: 0.55em;
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: rgba(217, 184, 255, 0.28);
  transition: background-color 0.4s var(--ease), box-shadow 0.4s var(--ease),
    transform 0.5s var(--ease-orbit);
}
.footer__link:hover {
  color: var(--paper);
  transform: translateX(3px);
}
html[dir='rtl'] .footer__link:hover {
  transform: translateX(-3px);
}
.footer__link:hover::before {
  background: var(--violet-bright);
  box-shadow: 0 0 10px var(--violet);
  transform: scale(1.25);
}
.footer__link svg {
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
html[dir='rtl'] .footer__link svg {
  transform: scaleX(-1) translateX(-4px);
}
.footer__link:hover svg {
  opacity: 0.6;
  transform: translateX(0);
}
html[dir='rtl'] .footer__link:hover svg {
  transform: scaleX(-1) translateX(0);
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  padding-block-start: clamp(1.5rem, 2.5vw, 2rem);
  border-block-start: 1px solid var(--line-soft);
  font-size: 0.8rem;
  color: var(--paper-muted);
}
.footer__bottom a:hover {
  color: var(--paper);
}
.footer__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.1rem;
}
/* A live clock in Berlin — the instrument keeping its own time. */
.footer__clock {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  color: var(--paper-48);
}
.footer__clock::before {
  content: '';
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: var(--violet);
  box-shadow: 0 0 8px var(--violet);
  animation: twinkle 3.2s var(--ease-inout) infinite;
}

/* ══ The carrier ══════════════════════════════════════════════════════════
   There is a hidden thing at /signal-zero/ that this site does not link to.
   Two states leak onto the surface and nothing else does: the whole page dims
   for half a second on the way in, and anyone who has been inside finds one
   small mark beside the clock afterwards.
   Deleting js/signal.js and this block removes every trace of it. */

.is-tuning body {
  animation: signal-tune 0.62s var(--ease-inout) forwards;
}

@keyframes signal-tune {
  to {
    opacity: 0;
    filter: brightness(1.6) blur(6px);
  }
}

/* Coming back from the carrier. The screen is black when the page opens and
   the site fades up out of it; only then does the type start misbehaving,
   switching on and off block by block for two seconds before it holds.

   The black is a real sheet over everything rather than a faded body, so the
   page comes out of darkness instead of out of transparency — and it is a
   pseudo-element on the root, so it exists before any module has run.

   Only block-level text flickers. Inline children (a, span, b) inherit it
   from their parent instead of animating on top, which is what stops nested
   text from going twice as dark as everything around it. */

.is-echo::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #000;
  pointer-events: none;
  animation: signal-blackout 1.15s var(--ease-inout) forwards;
}

@keyframes signal-blackout {
  0%, 14% { opacity: 1; }
  100% { opacity: 0; }
}

.is-echo :is(h1, h2, h3, h4, h5, h6, p, li, figcaption, blockquote, label, button, summary, dt, dd, th, td) {
  /* Starts as the black clears: the page has to be there before it can fail. */
  animation: signal-echo 2s steps(1, end) 0.85s forwards;
}

/* Out of step, so the page does not blink as one object. */
.is-echo :is(h2, li, dd, td, summary) { animation-delay: 0.94s; }
.is-echo :is(h3, p + p, figcaption, th) { animation-delay: 1.02s; }
.is-echo :is(h4, blockquote, label) { animation-delay: 1.11s; }

@keyframes signal-echo {
  0%, 9% {
    opacity: 0.1;
    text-shadow: 2px 0 rgba(255, 111, 216, 0.5), -2px 0 rgba(127, 212, 255, 0.5);
  }
  10%, 19% { opacity: 1; }
  20%, 26% {
    opacity: 0.22;
    text-shadow: 1.5px 0 rgba(255, 111, 216, 0.45), -1.5px 0 rgba(127, 212, 255, 0.45);
  }
  27%, 45% { opacity: 1; }
  46%, 51% { opacity: 0.4; }
  52%, 71% { opacity: 1; }
  72%, 75% { opacity: 0.7; }
  76%, 100% { opacity: 1; text-shadow: none; }
}

/* A flicker is a health question before it is a style one: no full-screen
   brightness, never white, and switched off entirely where motion is not
   wanted — there, the black simply lifts and the text is already there. */
@media (prefers-reduced-motion: reduce) {
  .is-echo::after {
    animation: signal-blackout 0.5s linear forwards;
  }

  .is-echo :is(h1, h2, h3, h4, h5, h6, p, li, figcaption, blockquote, label, button, summary, dt, dd, th, td) {
    animation: none;
  }
}

.signal-mark {
  inline-size: 6px;
  block-size: 6px;
  padding: 0;
  margin-inline-start: 0.15rem;
  border: 0;
  border-radius: 50%;
  background: var(--violet);
  opacity: 0.35;
  cursor: pointer;
  transition: opacity var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.signal-mark:hover,
.signal-mark:focus-visible {
  opacity: 1;
  box-shadow: 0 0 10px var(--violet);
  outline: none;
}

/* Back to the top of the sky. */
.toorbit {
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: 2.9rem;
  block-size: 2.9rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--paper-64);
  background: rgba(9, 6, 22, 0.6);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
    transform 0.5s var(--ease-orbit);
}
.toorbit::after {
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 1px dashed rgba(170, 140, 255, 0.3);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  animation: spin 16s linear infinite;
}
.toorbit:hover {
  color: var(--paper);
  border-color: rgba(178, 91, 240, 0.55);
  transform: translateY(-3px);
}
.toorbit:hover::after {
  opacity: 1;
}

/* A narrow viewport makes the same ellipse read as a steep dome that cuts
   through the columns. Widen it until the rim is almost a straight line
   again, and let the curtain fall further back. */
@media (max-width: 760px) {
  .horizon__planet {
    inset-inline: -120%;
    inset-block-end: -42%;
    block-size: 96%;
  }
  .horizon__aurora {
    inset-block-end: 46%;
    filter: blur(60px);
    opacity: 0.55;
  }
}

@media (max-width: 900px) {
  .footer__top {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .footer__brand {
    grid-column: 1 / -1;
  }
}
@media (max-width: 480px) {
  .footer__top {
    grid-template-columns: 1fr;
    gap: 2.25rem;
  }
}

/* ══ RESPONSIVE — the stage ═══════════════════════════════════════════════ */

@media (max-width: 1180px) {
  .orrery {
    --ap: clamp(14rem, min(30vw, 100svh - 24rem), 24rem);
  }
}

@media (max-width: 1000px) {
  .stage {
    min-block-size: auto;
    padding-block: calc(var(--nav-h) + clamp(1.25rem, 5vw, 3rem)) clamp(3rem, 9vw, 5rem);
  }
  .stage__inner {
    gap: clamp(1.5rem, 5vw, 2.5rem);
  }
  .orrery {
    --ap: min(74vw, 26rem);
    transform: none;
  }
  .descend {
    display: none;
  }
}

@media (max-width: 560px) {
  .orrery {
    --ap: min(75vw, 21rem);
    --orbit: calc(var(--ap) * 0.5 + 1.6rem);
  }
  /* On phones the desktop reward would make the mark almost as wide as the
     screen. Reduce the mark and return the Saturn sweep to a tighter, taller
     ellipse so its two arms visibly close around the artwork. */
  html.signal-zero-complete .orrery .orrery__emblem,
  .orrery.is-signal-complete .orrery__emblem {
    inline-size: min(calc(var(--ap) * 0.92), calc(100vw - 4.5rem));
  }
  html.signal-zero-complete .orrery .orrery__rings,
  .orrery.is-signal-complete .orrery__rings {
    inline-size: min(calc(var(--ap) * 1.55), calc(100vw - 1rem));
    aspect-ratio: 3.75 / 1;
  }
  .orrery__dot {
    inline-size: 1.95rem;
    block-size: 1.95rem;
    font-size: 0.56rem;
  }
  .orrery__read {
    font-size: 0.72rem;
    padding: 0.42rem 0.75rem;
  }
  /* Too narrow to flank the claim: the wires take a row each instead, so
     the line above and the line below still frame it. */
  .transmission--stage .transmission__inner {
    flex-wrap: wrap;
    gap: clamp(0.75rem, 3vw, 1.15rem);
  }
  .transmission--stage .transmission__claim,
  .transmission--stage .transmission__wire {
    flex: 1 0 100%;
  }
}

/* A phone held sideways answers every width query above as though it were a
   tablet, so it was handed the tablet instrument: a 26rem disc on a screen
   390px tall, inside a hero more than twice the height of the window. Here
   height is what is scarce, not width, so height is what sets the scale —
   the same rule the desktop stage already follows, restored for the case
   that needs it most. Also catches a short, narrow desktop window. */
@media (max-height: 620px) and (max-width: 1180px) {
  .stage {
    padding-block: calc(var(--nav-h) + 0.75rem) 1.75rem;
  }
  .stage__inner {
    gap: clamp(0.6rem, 2.2svh, 1.1rem);
  }
  .orrery {
    --ap: min(48svh, 62vw, 21rem);
    --orbit: calc(var(--ap) * 0.5 + clamp(1.1rem, 2.6svh, 1.8rem));
  }
}

/* ══ REDUCED MOTION ═══════════════════════════════════════════════════════
   Everything above is decoration over content that is already there. When a
   visitor asks for stillness, the instrument simply stops: no orbit, no
   drift, no travelling light. The layout is unchanged. */

@media (prefers-reduced-motion: reduce) {
  .sky__neb,
  .orrery__dial,
  .orrery__rings,
  .orrery__nodes,
  .orrery__dot,
  .orrery__halo,
  .aperture__bezel,
  .horizon__planet,
  .horizon__aurora,
  .footer__moon::before,
  .footer__moon::after,
  .toorbit::after,
  .burger__orbit,
  .eyebrow::before,
  .footer__clock::before,
  .descend i::after,
  .transmission__wire::after,
  .orrery__sound[aria-pressed='true'] .orrery__wave i,
  .cta__rings .cta__ring--dashed,
  .gate__mark {
    animation: none !important;
  }
  .aperture__layer {
    transition: opacity 0.2s linear !important;
    clip-path: none !important;
    opacity: 0;
  }
  .aperture__layer.is-current {
    opacity: 1;
  }
  .spark {
    display: none;
  }
  .constellation__line {
    stroke-dashoffset: 0;
  }
  .constellation__star {
    opacity: 1;
    transform: none;
  }
  .constellation__spark {
    display: none;
  }
}
