/* Render viewport ownership.
 *
 * The graph surface is an interaction plane, not selectable page content.
 * Browser text/callout selection is disabled across the surface while graph
 * nodes keep their explicit Rust/Leptos activation semantics. Labels and the
 * coloured strands never participate in hit testing: a connection is reached
 * through its single composite hit path, so a pair carrying three relations is
 * still one target rather than three competing ones.
 *
 * The embedded renderer is a square spatial instrument. Fullscreen/expanded
 * modes are separate view states and keep owning the physical screen instead.
 */

.renderer-surface--wasm,
.renderer-surface--wasm * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* `touch-action` is decided by the browser when a touch gesture starts; setting
 * it only after Rust crosses the drag threshold is too late on Safari/Chrome.
 * An active graph therefore owns pan gestures from pointer-down so rotating the
 * world can never become document scrolling mid-gesture. Blocking overlays put
 * the surface back under normal page gesture ownership via `data-pointer-blocked`. */
.renderer-surface--wasm[data-pointer-blocked="false"] {
  touch-action: none;
}

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

/* A blocking runtime error makes the whole render plane observational only.
 * Rust also applies the native `inert` attribute, which removes every node and
 * render control from pointer/keyboard activation; this rule is the visual and
 * pointer-level half of the same contract. Retry stays outside the surface. */
.renderer-surface--wasm[data-renderer-state="inactive"] {
  opacity: 0.42;
  pointer-events: none;
}

.renderer-surface--wasm .wasm-strand,
.renderer-surface--wasm .wasm-labels {
  pointer-events: none;
}

/* Normal page composition is square by contract. `render-controls.css` owns
 * fullscreen and iPhone expanded dimensions, so exclude those states here. */
.graph-stage:has(> .renderer-surface--wasm) {
  min-height: 0;
}

.renderer-surface.renderer-surface--wasm:not(:fullscreen):not([data-expanded="true"]) {
  width: 100%;
  height: auto;
  min-height: 0;
  max-height: none;
  aspect-ratio: 1 / 1;
}

/* Structurally the viewport groups heading + stage, but on tablet/desktop it
 * disappears from layout so the existing graph grid keeps its original
 * heading/stage/details geometry. */
.graph-viewport {
  display: contents;
}

@media (max-width: 719px) {
  .graph-section {
    padding-top: 0;
  }

  .graph-layout {
    display: block;
    margin-top: 0;
  }

  .graph-viewport {
    display: grid;
    width: 100%;
    height: auto;
    min-height: 0;
    grid-template-rows: auto auto;
    align-items: stretch;
  }

  .graph-viewport > .section-heading {
    grid-row: 1;
    min-height: 0;
  }

  .graph-viewport > .graph-stage {
    grid-row: 2;
    width: 100%;
    height: auto;
    min-height: 0;
    aspect-ratio: 1 / 1;
  }

  /* Runtime failures become a genuine middle row instead of an overlay. */
  .graph-viewport:has(> .runtime-banner) {
    grid-template-rows: auto auto auto;
  }

  .graph-viewport > .runtime-banner {
    grid-row: 2;
    margin: 0 0 0.75rem;
  }

  .graph-viewport:has(> .runtime-banner) > .graph-stage {
    grid-row: 3;
  }

  .renderer-surface.renderer-surface--wasm:not(:fullscreen):not([data-expanded="true"]) {
    width: 100%;
    height: auto;
    min-height: 0;
    max-height: none;
    aspect-ratio: 1 / 1;
  }

  /* The graph gets the stage minus the telemetry line. The overlay stays
     anchored to the surface edge where the control contract puts it; what
     changes is that the render layers stop reaching underneath it, so the
     bottom of the graph is never read through a pill of text. */
  .graph-viewport .renderer-surface--wasm {
    --telemetry-strip: 2.75rem;
  }

  .graph-viewport .renderer-gpu-canvas,
  .graph-viewport .renderer-svg-layer {
    /* Both layers are given the same computed height rather than being left to
       resolve one. The square surface provides a definite used height, so this
       subtraction stays identical for canvas and SVG. */
    height: calc(100% - var(--telemetry-strip, 0px));
  }

  /* The heading is a caption on a phone, not a billboard. */
  .graph-viewport > .section-heading {
    gap: 0.5rem;
    padding-bottom: 0.35rem;
  }

  .graph-viewport > .section-heading h2 {
    margin: 0.15rem 0 0;
    max-width: none;
    font-size: clamp(1.35rem, 6vw, 1.9rem);
    line-height: 1.05;
  }

  .graph-viewport > .section-heading .kicker {
    font-size: 0.58rem;
  }

  .graph-layout > .node-details {
    margin-top: 0.85rem;
  }
}

/* At desktop widths the new structural wrapper is layout-transparent. The
 * explicit columns preserve the existing full-width heading followed by the
 * renderer/details pair. */
@media (min-width: 980px) {
  .graph-layout .section-heading,
  .graph-layout .runtime-banner {
    grid-column: 1 / -1;
  }

  .graph-layout .graph-stage {
    grid-column: 1;
  }

  .graph-layout > .node-details {
    grid-column: 2;
  }
}
