.entity-field--wasm {
  position: fixed;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
}

.entity-field__orb {
  position: absolute;
  left: 50%;
  top: 44%;
  width: min(76vw, 68rem);
  aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background:
    radial-gradient(circle at 48% 46%, rgba(255,255,255,.16), transparent 2%),
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--accent), transparent 55%), transparent 29%),
    radial-gradient(circle at 50% 50%, rgba(5,8,13,.08), rgba(5,8,13,.86) 68%);
  filter: blur(.2px);
  opacity: .42;
}

.entity-field__grid {
  position: absolute;
  inset: -10%;
  background-image:
    linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(circle at 50% 44%, #000 0 28%, transparent 72%);
  opacity: .6;
}

.renderer-surface--wasm {
  isolation: isolate;
  overflow: hidden;
}

.wasm-graph {
  display: block;
  width: 100%;
  height: 100%;
  min-height: inherit;
  user-select: none;
  touch-action: manipulation;
}

/* One connection per endpoint pair. The strands inside it carry the relation
   colours; the hit path is what a pointer or the keyboard actually reaches, so
   a pair with three relations is still one target. */
.wasm-connection {
  cursor: pointer;
  outline: none;
}

.wasm-connection__hit {
  fill: none;
  stroke: transparent;
  stroke-width: 11;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.wasm-strand {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-opacity: .72;
  vector-effect: non-scaling-stroke;
  pointer-events: none;
}

.wasm-connection:hover .wasm-strand,
.wasm-connection:focus-visible .wasm-strand,
.wasm-connection--selected .wasm-strand {
  stroke-width: 1.8;
  stroke-opacity: 1;
}

.wasm-connection:focus-visible .wasm-connection__hit {
  stroke: rgba(226, 232, 255, .22);
}

.wasm-node {
  cursor: pointer;
  outline: none;
}

.wasm-node__halo {
  fill: color-mix(in srgb, currentColor, transparent 93%);
  stroke: transparent;
  transition: fill 160ms ease, stroke 160ms ease;
}

/* The fog of a failing default branch. It stays close to the body because the
   failure is a fact about one system: haze reaching the neighbours would say
   something the graph does not know. Colour is never the only carrier — the
   node's accessible name and the toast both say it in words. */
.wasm-node__fog {
  fill: color-mix(in srgb, var(--failure), transparent 84%);
  pointer-events: none;
}

.wasm-node__fog--inner {
  fill: color-mix(in srgb, var(--failure), transparent 68%);
}

/* A system with failing checks in flight. The WebGPU renderer strikes it with
   an asteroid every few seconds; this ring is what says so in between — and the
   only thing that says it at all where there is no canvas, under reduced motion
   or on the SVG fallback. Broken rather than solid, so it never reads as the
   continuous state the fog reports. */
.wasm-node__impact {
  fill: none;
  stroke: color-mix(in srgb, var(--failure), transparent 42%);
  stroke-width: 1.1;
  stroke-dasharray: 3 7;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}

.wasm-node__core {
  fill: #0b0e13;
  stroke: color-mix(in srgb, currentColor, white 18%);
  stroke-width: 1.2;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 9px color-mix(in srgb, currentColor, transparent 45%));
  transition: transform 160ms ease, fill 160ms ease;
  transform-box: fill-box;
  transform-origin: center;
}

/* The node group carries its position, so one transition moves the soma, its
   halo and its label together when the focus changes. */
.wasm-node {
  transition:
    transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
}

.wasm-node__core,
.wasm-node__halo {
  transition: r 720ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* While rotating, positions come straight from the pointer: a transition here
   would lag behind the drag. */
.renderer-surface--wasm[data-dragging="true"] .wasm-connection,
.renderer-surface--wasm[data-dragging="true"] .wasm-node,
.renderer-surface--wasm[data-dragging="true"] .wasm-node__core,
.renderer-surface--wasm[data-dragging="true"] .wasm-node__halo {
  transition: none;
}

/* Depth cues for the focus-relative 3D space. */
.wasm-node--far .wasm-node__core {
  opacity: 0.62;
}

.wasm-node--far .wasm-node__halo {
  opacity: 0.4;
}

/* Labels live in their own layer above every soma, so a nearer node can no
   longer cover a farther node's label. Reveal state comes from the hover
   signal rather than a descendant selector, which that split rules out. */
.wasm-labels {
  pointer-events: none;
}

/* Beyond the focus and its direct neighbours, labels would overlap without a
   per-frame collision solve that a rotating space cannot afford, so they
   reveal on demand instead. */
.wasm-node__label--quiet {
  opacity: 0;
  transition: opacity 140ms ease;
}

.wasm-node__label--revealed {
  opacity: 1;
}

/* While the pointer is rotating the space, the SVG projection is mid-flight;
   taking it out of hit testing is what keeps a drag from landing as a click. */
.renderer-surface--wasm[data-dragging="true"] .renderer-svg-layer {
  pointer-events: none;
}

.renderer-surface--wasm {
  cursor: grab;
}

.renderer-surface--wasm[data-dragging="true"] {
  cursor: grabbing;
}

.wasm-node__label {
  fill: #c9ccd4;
  /* No font-size here on purpose: the layout sets it per label as an SVG
     presentation attribute, in design units. A rule here would override that
     attribute, and an inline style attribute would be dropped by the CSP,
     which carries no 'unsafe-inline' for style-src. */
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  letter-spacing: .04em;
  pointer-events: none;
}

.wasm-node__label--private {
  fill: var(--private);
}

.wasm-node:hover .wasm-node__halo,
.wasm-node:focus-visible .wasm-node__halo,
.wasm-node--selected .wasm-node__halo {
  fill: color-mix(in srgb, currentColor, transparent 82%);
  stroke: color-mix(in srgb, currentColor, transparent 62%);
}

.wasm-node:hover .wasm-node__core,
.wasm-node:focus-visible .wasm-node__core,
.wasm-node--selected .wasm-node__core {
  fill: color-mix(in srgb, currentColor, #090a0c 74%);
  transform: scale(1.18);
}

.renderer-surface--wasm[data-impulse]:not([data-impulse="0"])::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 2rem;
  height: 2rem;
  border: 1px solid color-mix(in srgb, var(--accent), white 12%);
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(.2);
  opacity: 0;
  animation: wasm-impulse 980ms cubic-bezier(.2,.72,.2,1);
  pointer-events: none;
}

.runtime-banner {
  display: flex;
  margin-top: 1rem;
  padding: .9rem 1rem;
  border: 1px solid var(--border);
  border-radius: .9rem;
  background: var(--surface);
}

.runtime-banner > div {
  display: grid;
  gap: .3rem;
}

.runtime-banner span,
.document-state {
  color: var(--muted);
}

.runtime-banner--error {
  border-color: color-mix(in srgb, var(--danger), transparent 45%);
}

.accessible-graph {
  margin-top: 1rem;
  color: var(--muted);
}

.accessible-graph ol {
  display: grid;
  margin: .8rem 0 0;
  padding: 0;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .5rem;
  list-style: none;
}

.accessible-graph button {
  display: grid;
  width: 100%;
  padding: .8rem;
  gap: .2rem;
  border: 1px solid var(--border);
  border-radius: .8rem;
  color: var(--text);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
}

.accessible-graph button[aria-current="true"] {
  border-color: color-mix(in srgb, var(--accent), transparent 35%);
}

.accessible-graph button span {
  color: var(--muted);
  font-size: .72rem;
}

.runtime-footer {
  display: flex;
  padding: 1.25rem 0 2rem;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  color: var(--muted);
  font-size: .7rem;
}

@keyframes wasm-impulse {
  0% { opacity: .9; transform: translate(-50%, -50%) scale(.2); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(28); }
}

@media (prefers-reduced-motion: reduce) {
  .renderer-surface--wasm::after { animation: none !important; }
  .wasm-node__core, .wasm-node__halo { transition: none; }
}

/* Rust WebGPU frame strategy */
.renderer-surface--wasm {
  position: relative;
  isolation: isolate;
}

.renderer-gpu-canvas,
.renderer-svg-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.renderer-gpu-canvas {
  z-index: 0;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms ease;
}

.renderer-svg-layer { z-index: 1; }
.renderer-surface--wasm[data-renderer="webgpu"] .renderer-gpu-canvas { opacity: 1; }

.renderer-surface--wasm[data-renderer="webgpu"] .wasm-strand,
.renderer-surface--wasm[data-renderer="webgpu"] .wasm-node__halo,
.renderer-surface--wasm[data-renderer="webgpu"] .wasm-node__core { opacity: 0; }

.renderer-surface--wasm[data-renderer="webgpu"] .wasm-node { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .renderer-gpu-canvas { display: none; }
}

/* A private node answers instead of showing an empty document panel, so its
   line must not read like the muted "nothing loaded yet" beside it. */
.document-state--private {
  color: var(--private);
}

/* Relation channels.
 *
 * The colour of a relation is keyed by its stable token rather than written
 * into the markup: the delivered policy carries no `unsafe-inline`, so a style
 * attribute would be refused and the swatch would lose exactly what it encodes.
 * These values are the ones docs/rendering/composite-edges.yml states, and
 * `xtask validate` fails if they drift from it or from the Rust channel table. */
.relation-legend__item[data-relation="ownership"],
.connection-relation[data-relation="ownership"] { --relation-color: #38BDF8; }

.relation-legend__item[data-relation="membership"],
.connection-relation[data-relation="membership"] { --relation-color: #3B82F6; }

.relation-legend__item[data-relation="containment"],
.connection-relation[data-relation="containment"] { --relation-color: #94A3B8; }

.relation-legend__item[data-relation="dependency"],
.connection-relation[data-relation="dependency"] { --relation-color: #F97316; }

.relation-legend__item[data-relation="reference"],
.connection-relation[data-relation="reference"] { --relation-color: #E879F9; }

.relation-legend__item[data-relation="implementation"],
.connection-relation[data-relation="implementation"] { --relation-color: #F43F5E; }

/* Relationship legend.
 *
 * Colour identifies a relation type, but it never carries the meaning alone:
 * the token, the name and the family are text. Discovery provenance is absent
 * on purpose — it is player history, not a fact between two endpoints. */
.relation-legend {
  position: absolute;
  z-index: 5;
  inset-inline-start: .85rem;
  inset-block-end: .85rem;
  display: flex;
  max-width: calc(100% - 14rem);
  flex-direction: column;
  align-items: flex-start;
  gap: .28rem;
  margin: 0;
  padding: .55rem .65rem;
  border-radius: .55rem;
  background: rgba(2, 4, 15, .54);
  box-shadow: 0 .7rem 2.4rem rgba(0, 0, 0, .2);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  list-style: none;
  color: var(--muted);
  font-size: .64rem;
  letter-spacing: .04em;
  line-height: 1.25;
  pointer-events: none;
}

.relation-legend__item {
  display: inline-flex;
  max-width: 100%;
  align-items: baseline;
  gap: .4rem;
  white-space: nowrap;
}

.relation-legend__swatch {
  align-self: center;
  width: .55rem;
  height: .55rem;
  border-radius: 2px;
  background: var(--relation-color);
  box-shadow: 0 0 10px color-mix(in srgb, var(--relation-color), transparent 45%);
}

.relation-legend__item strong {
  color: color-mix(in srgb, var(--relation-color), white 22%);
  font-weight: 500;
}

.relation-legend__token { opacity: .72; }
.relation-legend__family { opacity: .5; }
.relation-legend__count { opacity: .5; font-variant-numeric: tabular-nums; }

@media (max-width: 719px) {
  .relation-legend {
    inset-inline-start: .6rem;
    inset-block-end: calc(var(--telemetry-strip, 2.75rem) + .55rem);
    max-width: calc(100% - 1.2rem);
    gap: .2rem;
    padding: .45rem .5rem;
    font-size: .58rem;
    letter-spacing: .025em;
  }

  .relation-legend__item {
    gap: .3rem;
  }
}

/* Connection inspection: every relation the pair carries, each with its own
   direction and the relay origin that evidences it. */
.connection-note {
  margin: .6rem 0 0;
  color: var(--muted);
  font-size: .74rem;
}

.connection-relations {
  display: grid;
  gap: .6rem;
  margin: .9rem 0 0;
  padding: 0;
  list-style: none;
}

.connection-relation {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: .6rem;
}

.connection-relation__swatch {
  width: .5rem;
  height: .5rem;
  margin-top: .35rem;
  border-radius: 2px;
  background: var(--relation-color);
  box-shadow: 0 0 10px color-mix(in srgb, var(--relation-color), transparent 45%);
}

.connection-relation div {
  display: grid;
  gap: .15rem;
}

.connection-relation strong {
  color: color-mix(in srgb, var(--relation-color), white 22%);
  font-size: .8rem;
  font-weight: 500;
}

.connection-relation__direction { font-size: .74rem; }

.connection-relation__evidence {
  color: var(--muted);
  font-size: .68rem;
}

/* A toast that reports a failing run hands the run over. */
.graph-toast__link {
  display: inline-block;
  margin-top: 0.45rem;
  color: color-mix(in srgb, var(--failure), white 26%);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.66rem;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--failure), transparent 55%);
}

.graph-toast__link:hover,
.graph-toast__link:focus-visible {
  color: color-mix(in srgb, var(--failure), white 46%);
  outline: none;
}
