/* backdrop-orbits.css: the engine's stylesheet: structure, a plain default look, motion rules.
   A skin (skins/<name>.css) restyles the look under .backdrop-orbits[data-backdrop-skin="<name>"].
   See README.md. Two tiers of custom property:
     --backdrop-orbits-*        a host may set these (declared below with working defaults)
     --backdrop-orbits-paint-*  the script writes these; setting them by hand does nothing */

/* ---------- registered properties ---------- */
/* The avoid switch does NOT inherit: "none" on a column switches off that column's own zone, and the
   zones of the elements inside it (each named in data-backdrop-avoid) stay on. As an ordinary custom
   property it inherited, and switching a column off on a phone switched off its eyebrow, paragraph,
   buttons and notes too. */
@property --backdrop-orbits-avoid { syntax: "*"; inherits: false; }

/* ---------- host tokens: zero specificity, so a host that sets them anywhere up the tree wins ---------- */
:where(:root) {
  /* where the system sits inside the root, and how big it is. Lengths may use cqw/cqh/cqmin:
     the root is a size container, so they resolve against the backdrop, not the window. */
  --backdrop-orbits-x: 50%;
  --backdrop-orbits-y: 50%;
  --backdrop-orbits-r1: 21cqmin;
  --backdrop-orbits-r2: 31cqmin;
  --backdrop-orbits-r3: 41cqmin;
  --backdrop-orbits-r4: 51cqmin;
  /* the orbital plane: tilt is how far it lies back, roll turns the whole ellipse on screen */
  --backdrop-orbits-tilt: 62deg;
  --backdrop-orbits-roll: -8deg;
  --backdrop-orbits-perspective: 1500px;
  /* bodies float this far above their ring, measured along the plane's normal */
  --backdrop-orbits-lift: 14px;
  --backdrop-orbits-body-scale: 1;
  /* far side: opacity of a body and of a ring at the very back (1 = no fade) */
  --backdrop-orbits-far: .5;
  --backdrop-orbits-far-ring: .3;
  /* multipliers: orbit speed, pointer parallax strength */
  --backdrop-orbits-speed: 1;
  --backdrop-orbits-parallax: 1;
  /* on an element named by data-backdrop-avoid: none switches that zone off (per breakpoint) */
  --backdrop-orbits-avoid: auto;
  /* the overlay (tooltip and chips) stacks this high among the host's children */
  --backdrop-orbits-overlay-z: 5;
  /* with data-backdrop-front: off switches front mode off (per breakpoint) */
  --backdrop-orbits-front: auto;
  /* the height of the host's fixed header over the viewport's top: tooltips and chips keep below it,
     and the loop stops once less than 24 px of the root shows beneath it */
  --backdrop-orbits-inset-top: 0px;
  /* top: the tooltip is pinned across the screen under that header instead of beside its tool (phones,
     where every place beside a tool is the headline). none: beside the tool. */
  --backdrop-orbits-tip-dock: none;
  /* the plain default look (the engine's own, used until a skin restyles it) */
  --backdrop-orbits-ink: #1D2433;
  --backdrop-orbits-line: rgba(29, 36, 51, .22);
  --backdrop-orbits-accent: #3D6BE0;
  --backdrop-orbits-card: #FFFFFF;
  --backdrop-orbits-font: system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* ---------- the source list is data, never shown ---------- */
[data-backdrop-orbits] [data-backdrop-body],
[data-backdrop-orbits] [data-backdrop-satellite],
[data-backdrop-orbits] [data-backdrop-hub] { display: none; }

/* ---------- structure ---------- */
.backdrop-orbits {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none;
  container-type: size; contain: strict; isolation: isolate;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.backdrop-orbits-stage, .backdrop-orbits-sky, .backdrop-orbits-decor,
.backdrop-orbits-shadows, .backdrop-orbits-spokes, .backdrop-orbits-veil, .backdrop-orbits-bodies, .backdrop-orbits-fx {
  position: absolute; inset: 0; pointer-events: none;
}
.backdrop-orbits-stage { overflow: hidden; }
/* everything the script moves every frame gets its own layer, so moving it never repaints its neighbours */
.backdrop-orbits-sky, .backdrop-orbits-decor, .backdrop-orbits-body, .backdrop-orbits-spoke, .backdrop-orbits-shadow,
.backdrop-orbits-hub, .backdrop-orbits-satellite, .backdrop-orbits-packet-at.is-on, .backdrop-orbits-spark.is-on, .backdrop-orbits-chip.is-on, .backdrop-orbits-tip.is-show { will-change: transform, opacity; }
/* the overlay: a second .backdrop-orbits the engine adds to the host, above its content, holding the
   tooltip and the chips. The script gives it the root's box (inline); it never takes the pointer */
.backdrop-orbits-overlay { pointer-events: none; }
/* the sky is also the catcher: a click that reaches it (through the host's pass-through wrappers)
   is a click on empty backdrop */
.backdrop-orbits-sky { pointer-events: auto; inset: -40px; }
/* particles: canvases drawn once and moved by the compositor (see the script) */
.backdrop-orbits-dust { position: absolute; inset: 0; pointer-events: none; }
.backdrop-orbits-particles { position: absolute; display: block; pointer-events: none; visibility: hidden; transition: visibility 0s linear 1.1s; }
.backdrop-orbits-particles.is-on { visibility: visible; transition: none; }
.backdrop-orbits-particles { will-change: transform, opacity; }
/* the plane's dust: beside the plane, given the plane's transform by the script (see render) */
.backdrop-orbits-plane-dust { position: absolute; inset: 0; pointer-events: none; transition: opacity 1s ease .25s; }
.backdrop-orbits-particles.is-plane { left: 0; top: 0; transform-origin: 0 0; }
.backdrop-orbits-particles.is-set { left: 0; top: 0; width: 0; height: 0; }
/* lite mode (the governor: see the script): the dust in the tilted plane is the costliest thing to composite.
   It fades out, then the script hides it (hidden) and stops writing it; leaving lite it fades back in */
.backdrop-orbits.is-lite .backdrop-orbits-plane-dust { opacity: 0; transition: opacity .8s ease; }
/* lite: shadows, spokes and satellites are drawn into the backdrop's own layer. Every layer on the page is
   rebuilt in every frame the page draws, whoever asked for it; these move a few times a second there */
.backdrop-orbits.is-lite .backdrop-orbits-shadow, .backdrop-orbits.is-lite .backdrop-orbits-spoke, .backdrop-orbits.is-lite .backdrop-orbits-satellite { will-change: auto; }
.backdrop-orbits-particle { position: absolute; left: 0; top: 0; background: center / 100% 100% no-repeat; }
.backdrop-orbits-particle-beat { position: absolute; left: 0; top: 0; width: 0; height: 0; }
.backdrop-orbits-sky-decor, .backdrop-orbits-sky-decor > *, .backdrop-orbits-decor > * { position: absolute; inset: 0; }
.backdrop-orbits-sky-decor { pointer-events: none; }

/* the plane is a 0x0 point at the system's centre; the script writes its 3D transform */
.backdrop-orbits-plane { position: absolute; left: 0; top: 0; width: 0; height: 0; transform-origin: 0 0; will-change: transform; pointer-events: none; }
.backdrop-orbits-disc, .backdrop-orbits-wave, .backdrop-orbits-flash { position: absolute; left: 0; top: 0; border-radius: 50%; pointer-events: none; }
/* the burst's flash on each ring (sized by the script: the ring's radius) */
.backdrop-orbits-flash { opacity: 0; box-sizing: border-box; }
/* the burst round a covered hub: rings leaving the anchor's edge (placed and sized by the script) */
.backdrop-orbits-halo { position: absolute; left: 0; top: 0; opacity: 0; pointer-events: none; box-sizing: border-box; transform-origin: 50% 50%; }
/* rings and the skin's engraving: one svg whose origin is the system's centre. Its far half (the top,
   in the plane's own coordinates) is faded by an svg mask the script sizes from --backdrop-orbits-far-ring */
.backdrop-orbits-plane-art { position: absolute; overflow: visible; pointer-events: none; }
/* a host reset like img, svg { max-width: 100% } makes anything inside the 0x0 plane 0 px wide */
.backdrop-orbits-plane-art, .backdrop-orbits-particles { max-width: none; max-height: none; }
.backdrop-orbits-ring, .backdrop-orbits-plane-decor { transform-box: view-box; transform-origin: 0 0; }
.backdrop-orbits-disc, .backdrop-orbits-wave {
  width: calc(var(--backdrop-orbits-paint-rmax) * 2); height: calc(var(--backdrop-orbits-paint-rmax) * 2);
  margin: calc(var(--backdrop-orbits-paint-rmax) * -1) 0 0 calc(var(--backdrop-orbits-paint-rmax) * -1);
}
.backdrop-orbits-wave { opacity: 0; }
.backdrop-orbits-ripple { position: absolute; left: -30px; top: -30px; width: 60px; height: 60px; border-radius: 50%; opacity: 0; pointer-events: none; }

/* screen-space pieces: each is a 0x0 anchor the script moves with transform */
.backdrop-orbits-spoke { position: absolute; left: 0; top: 0; width: 100px; height: 2px; margin-top: -1px; transform-origin: 0 50%; opacity: 0; }
.backdrop-orbits-shadow { position: absolute; left: 0; top: 0; width: 0; height: 0; opacity: 0; }
.backdrop-orbits-body, .backdrop-orbits-satellite, .backdrop-orbits-hub,
.backdrop-orbits-packet-at, .backdrop-orbits-chip, .backdrop-orbits-spark, .backdrop-orbits-bit, .backdrop-orbits-tip {
  position: absolute; left: 0; top: 0; transform-origin: 0 0;
}
/* a packet: the script places the wrapper (-packet-at); the skin draws the packet inside it, and may
   scale, rotate or transform it freely (centred on its own middle, so give it a negative margin of
   half its size, as the default look does) */
.backdrop-orbits-packet { position: absolute; left: 0; top: 0; }
.backdrop-orbits-packet-at { opacity: 0; }
.backdrop-orbits-body { opacity: 0; }
/* the card is centred on its anchor with the individual `translate` property, which leaves
   `transform` on the card free for a skin to use */
.backdrop-orbits-card { position: absolute; left: 0; top: 0; translate: -50% -50%; pointer-events: auto; cursor: pointer; white-space: nowrap; }
.backdrop-orbits-hub-core { pointer-events: auto; cursor: pointer; }
.backdrop-orbits-hub > * { position: absolute; left: 0; top: 0; translate: -50% -50%; }
.backdrop-orbits-icon { display: grid; place-items: center; flex: none; }
.backdrop-orbits-icon svg, .backdrop-orbits-hub-core svg { display: block; width: 60%; height: 60%; }
.backdrop-orbits-ping, .backdrop-orbits-hub-ping { position: absolute; inset: -3px; border-radius: inherit; opacity: 0; pointer-events: none; }
.backdrop-orbits-packet, .backdrop-orbits-spark, .backdrop-orbits-bit, .backdrop-orbits-chip { opacity: 0; }
.backdrop-orbits-packet.is-on, .backdrop-orbits-spark.is-on, .backdrop-orbits-chip.is-on { opacity: 1; }
.backdrop-orbits-spark-tail { position: absolute; right: 0; top: 0; width: 60px; transform-origin: 100% 50%; }
/* the bodies stack by depth (z-index 1000 +/- their z); the tooltip and chips must clear all of them */
.backdrop-orbits-tip { z-index: 3000; opacity: 0; visibility: hidden; transition: opacity .18s ease, visibility 0s linear .18s; white-space: normal; }
.backdrop-orbits-tip.is-show { opacity: 1; visibility: visible; transition: opacity .18s ease; }
/* docked (--backdrop-orbits-tip-dock: top): one card across the screen under the host's header, the
   name and the line run together so it stays two lines tall at most. This box is the engine's (the
   script sizes it); a skin keeps its colours, fonts and frame, and may restyle .is-dock further. */
.backdrop-orbits-tip.is-dock { padding-top: 5px !important; padding-bottom: 6px !important; line-height: 1.25 !important; }
.backdrop-orbits-tip.is-dock .backdrop-orbits-tip-name { display: inline-flex !important; margin: 0 .55em 0 0 !important; vertical-align: top; }
.backdrop-orbits-tip.is-dock .backdrop-orbits-tip-line { display: inline !important; }
.backdrop-orbits-probe { position: absolute; inset: 0; visibility: hidden; pointer-events: none; }
.backdrop-orbits-probe > i { position: absolute; left: 0; top: 0; height: 0; }

/* ---------- the plain default look (zero specificity, so any skin rule wins) ---------- */
:where(.backdrop-orbits) :where(.backdrop-orbits-ring circle) { fill: none; stroke: var(--backdrop-orbits-line); stroke-width: 1.5; }
:where(.backdrop-orbits) :where(.backdrop-orbits-ring .backdrop-orbits-ring-glow) { stroke: var(--backdrop-orbits-accent); stroke-width: 3; opacity: 0; }
:where(.backdrop-orbits) :where(.backdrop-orbits-wave) { border: 2px solid var(--backdrop-orbits-accent); }
/* by default the flash wears the colour and width the skin gave .backdrop-orbits-ring-glow (the script
   reads them into the -paint- values); a skin may style .backdrop-orbits-flash itself */
:where(.backdrop-orbits) :where(.backdrop-orbits-flash) {
  border: var(--backdrop-orbits-paint-flash-width, 3px) solid var(--backdrop-orbits-paint-flash-color, var(--backdrop-orbits-accent));
}
:where(.backdrop-orbits) :where(.backdrop-orbits-halo) {
  border-radius: 22px; border: 2.5px solid var(--backdrop-orbits-accent);
  box-shadow: 0 0 18px color-mix(in srgb, var(--backdrop-orbits-accent) 45%, transparent);
}
:where(.backdrop-orbits) :where(.backdrop-orbits-ripple) { border: 2px solid var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-spoke) { background: linear-gradient(90deg, transparent 10%, var(--backdrop-orbits-line)); }
:where(.backdrop-orbits) :where(.backdrop-orbits-spoke)::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent 10%, var(--backdrop-orbits-accent)); opacity: 0; transition: opacity .25s; }
:where(.backdrop-orbits) :where(.backdrop-orbits-spoke.is-lit)::after { opacity: 1; }
:where(.backdrop-orbits) :where(.backdrop-orbits-card) {
  display: flex; align-items: center; gap: 8px; padding: 6px 12px 6px 6px; border-radius: 12px;
  background: var(--backdrop-orbits-card); border: 1px solid var(--backdrop-orbits-line); color: var(--backdrop-orbits-ink);
  font: 500 12px/1 var(--backdrop-orbits-font); transition: border-color .2s, box-shadow .2s;
}
:where(.backdrop-orbits) :where(.backdrop-orbits-body.is-hover .backdrop-orbits-card) { border-color: var(--backdrop-orbits-accent); box-shadow: 0 6px 18px -8px rgba(0, 0, 0, .35); }
:where(.backdrop-orbits) :where(.backdrop-orbits-icon) { width: 26px; height: 26px; border-radius: 8px; background: color-mix(in srgb, var(--backdrop-orbits-accent) 14%, transparent); color: var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-icon svg, .backdrop-orbits-hub-core svg) { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
:where(.backdrop-orbits) :where(.backdrop-orbits-ping) { border: 2px solid var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-hub-core) { display: grid; place-items: center; width: 56px; height: 56px; border-radius: 50%; background: var(--backdrop-orbits-accent); color: #fff; }
:where(.backdrop-orbits) :where(.backdrop-orbits-hub-ping) { border-radius: 50%; border: 2px solid var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-satellite) { width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%; background: var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-shadow) { width: 80px; height: 80px; margin: -40px 0 0 -40px; border-radius: 50%; background: radial-gradient(closest-side, rgba(0, 0, 0, .14), transparent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-packet) { width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%; background: var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-chip-text) {
  display: block; translate: -50% -100%; padding: 6px 10px; border-radius: 999px; white-space: nowrap;
  background: var(--backdrop-orbits-ink); color: #fff; font: 600 12px/1 var(--backdrop-orbits-font);
}
:where(.backdrop-orbits) :where(.backdrop-orbits-spark-head) { position: absolute; left: -4px; top: -4px; width: 8px; height: 8px; border-radius: 50%; background: var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-spark-tail) { height: 2px; margin-top: -1px; background: linear-gradient(90deg, transparent, var(--backdrop-orbits-accent)); }
:where(.backdrop-orbits) :where(.backdrop-orbits-bit) { width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%; background: var(--backdrop-orbits-accent); }
:where(.backdrop-orbits) :where(.backdrop-orbits-tip) {
  width: max-content; max-width: 230px; padding: 9px 12px; border-radius: 10px; background: var(--backdrop-orbits-card);
  border: 1px solid var(--backdrop-orbits-line); color: var(--backdrop-orbits-ink); font: 12.5px/1.4 var(--backdrop-orbits-font);
  box-shadow: 0 10px 24px -12px rgba(0, 0, 0, .35);
}
:where(.backdrop-orbits) :where(.backdrop-orbits-tip-name) { display: block; font-weight: 700; margin-bottom: 2px; }

/* ---------- entrance: start invisible, then sky fades in, rings draw outward, hub arrives.
   (The bodies' own arrival is written by the script, along their orbits from the hub.) ---------- */
.backdrop-orbits-sky, .backdrop-orbits-decor, .backdrop-orbits-dust { transition: opacity .9s ease; }
.backdrop-orbits-ring { transition: opacity .8s ease calc(.1s + var(--backdrop-orbits-paint-i) * .11s), transform .9s cubic-bezier(.2, .7, .2, 1) calc(.1s + var(--backdrop-orbits-paint-i) * .11s); }
.backdrop-orbits-disc, .backdrop-orbits-plane-decor { transition: opacity 1s ease .25s, transform 1.1s cubic-bezier(.2, .7, .2, 1) .2s; }
.backdrop-orbits-decor[hidden] { display: none; }
.backdrop-orbits-hub > * { transition: opacity .6s ease .2s, scale .7s cubic-bezier(.3, 1.4, .5, 1) .2s; }
.backdrop-orbits.is-entering .backdrop-orbits-sky,
.backdrop-orbits.is-entering .backdrop-orbits-dust,
.backdrop-orbits.is-entering .backdrop-orbits-plane-dust,
.backdrop-orbits.is-entering .backdrop-orbits-decor { opacity: 0; }
.backdrop-orbits.is-entering .backdrop-orbits-ring,
.backdrop-orbits.is-entering .backdrop-orbits-disc,
.backdrop-orbits.is-entering .backdrop-orbits-plane-decor { opacity: 0; transform: scale(.45); }
.backdrop-orbits.is-entering .backdrop-orbits-hub > * { opacity: 0; scale: .5; }
/* motion turned off before or during the entrance: the finished frame, at once */
.backdrop-orbits.is-instant *, .backdrop-orbits.is-instant *::before, .backdrop-orbits.is-instant *::after { transition: none !important; }
/* a skin change fades the new sky in */
.backdrop-orbits.is-reskin .backdrop-orbits-sky, .backdrop-orbits.is-reskin .backdrop-orbits-decor, .backdrop-orbits.is-reskin .backdrop-orbits-dust { opacity: 0; transition: none; }

/* ---------- motion rules ---------- */
/* stopped for any reason (off screen, tab hidden, held still): a skin's own CSS animations hold too */
.backdrop-orbits.is-paused *, .backdrop-orbits.is-paused *::before, .backdrop-orbits.is-paused *::after { animation-play-state: paused !important; }
/* reduced motion: no animation at all, and no entrance; the base styles ARE the finished frame */
.backdrop-orbits.is-reduced *, .backdrop-orbits.is-reduced *::before, .backdrop-orbits.is-reduced *::after { animation: none !important; transition-property: opacity, visibility !important; }

/* ---------- forced colours: a quiet line drawing; no canvas, no glow ---------- */
@media (forced-colors: active) {
  .backdrop-orbits-dust, .backdrop-orbits-particles, .backdrop-orbits-decor, .backdrop-orbits-sky-decor, .backdrop-orbits-shadows,
  .backdrop-orbits-spokes, .backdrop-orbits-disc, .backdrop-orbits-plane-decor, .backdrop-orbits-packet,
  .backdrop-orbits-spark, .backdrop-orbits-bit, .backdrop-orbits-satellite, .backdrop-orbits-wave, .backdrop-orbits-ripple,
  .backdrop-orbits-flash, .backdrop-orbits-halo { display: none !important; }
  .backdrop-orbits .backdrop-orbits-ring circle { stroke: GrayText !important; }
  .backdrop-orbits .backdrop-orbits-ring .backdrop-orbits-ring-glow { stroke: Highlight !important; }
  .backdrop-orbits .backdrop-orbits-card, .backdrop-orbits .backdrop-orbits-tip, .backdrop-orbits .backdrop-orbits-chip-text,
  .backdrop-orbits .backdrop-orbits-hub-core { background: Canvas !important; color: CanvasText !important; border: 1px solid CanvasText !important; box-shadow: none !important; }
}
