/* Render-viewport controls.
 *
 * One overlay owns every control that belongs to the render view, so the edge
 * offsets are declared here once instead of being scattered across unrelated
 * components. The overlay is a child of the render surface and is laid out as
 * a grid: the first row carries the way back on one edge and the view controls
 * on the other, the last row carries telemetry, and the elastic row between
 * them is what makes an overlap geometrically impossible at any viewport rather
 * than merely unlikely.
 *
 * Nothing here is positioned in graph space. Camera rotation, focus
 * transitions and the WebGPU/SVG split all leave these controls where the
 * viewport edges put them.
 */

.render-controls {
  --render-control-inset: 0.85rem;
  --render-control-surface: rgba(2, 4, 15, 0.72);
  --render-control-border: rgba(116, 139, 255, 0.22);
  --render-control-ink: rgba(190, 201, 244, 0.72);

  position: absolute;
  z-index: 6;
  inset: 0;
  display: grid;
  padding: var(--render-control-inset);
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "way-back controls"
    ". ."
    "telemetry telemetry";
  gap: 0.5rem;
  /* The overlay itself is inert: only the controls take the pointer, so the
     graph keeps every hit the overlay merely covers. */
  pointer-events: none;
}

.render-controls__group {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  pointer-events: none;
}

.render-controls__group--top-right {
  grid-area: controls;
  justify-self: end;
}

/* The way back leaves the profile; the controls opposite it change how the
   current one is drawn. Sharing an edge made a navigation action read as one
   more view toggle, so they sit on opposite corners. */
.render-controls__group--way-back {
  grid-area: way-back;
  justify-self: start;
}

/* One control system: the way back and the fullscreen toggle share radius,
   border, surface, hover and focus treatment, and differ only in glyph size. */
.render-control {
  display: grid;
  width: 2.25rem;
  height: 2.25rem;
  place-items: center;
  padding: 0;
  border: 1px solid var(--render-control-border);
  border-radius: 999px;
  background: var(--render-control-surface);
  box-shadow: 0 0.4rem 1.4rem rgba(0, 0, 0, 0.2);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--render-control-ink);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.68rem;
  line-height: 1;
  cursor: pointer;
  pointer-events: auto;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}

/* The chevron is a thin glyph in an otherwise empty circle, so it reads far
   smaller than the control actually is; it carries its own size. */
.render-control--way-back {
  font-size: 1.35rem;
}

.render-control:hover,
.render-control:focus-visible {
  border-color: color-mix(in srgb, var(--accent), transparent 45%);
  background: rgba(6, 10, 28, 0.9);
  color: var(--text);
}

.render-control:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--accent), transparent 25%);
  outline-offset: 2px;
}

/* Renderer telemetry. Attached to the viewport, never to the graph, and never
   a control: it takes no pointer and no focus. */
.render-controls__telemetry {
  display: inline-flex;
  max-width: 100%;
  min-height: 1.85rem;
  margin: 0;
  padding: 0.4rem 0.62rem;
  align-items: center;
  grid-area: telemetry;
  justify-self: end;
  align-self: end;
  border: 1px solid rgba(116, 139, 255, 0.16);
  border-radius: 999px;
  background: rgba(2, 4, 15, 0.64);
  box-shadow: 0 0.7rem 2.4rem rgba(0, 0, 0, 0.24);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: rgba(190, 201, 244, 0.58);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.62rem;
  line-height: 1;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

/* The fullscreen surface is the render surface itself. The page's embedded
   sizing must not survive the promotion: a 34rem minimum height would overflow
   a landscape phone whose screen is shorter than that. */
.renderer-surface--wasm:fullscreen {
  min-height: 0;
  max-height: none;
  border-radius: 0;
}

/* The expansion used where there is no Fullscreen API — an iPhone will not
   promote an element, so without this the control would have nothing to do and
   the feature would simply not exist on the devices most likely to want it.
   `dvh` is what keeps it honest while Safari's address bar moves. */
.renderer-surface--wasm[data-expanded="true"] {
  position: fixed;
  z-index: 2147483000;
  inset: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  max-height: none;
  border-radius: 0;
  /* The surface's own gradient bottoms out at 99% alpha, which native
     fullscreen hides behind its backdrop but an in-page expansion does not:
     the page heading showed through. This is that missing backdrop. */
  background-color: #02030d;
  /* Without this a drag scrolls the page behind the expansion instead of
     rotating the graph, because the surface is only fixed, not modal. */
  touch-action: none;
}

/* Only when the surface fills the screen does it reach the physical edges, so
   the safe area is honoured there and nowhere else. Applying it to the embedded
   render would indent controls that sit nowhere near a notch. The base inset is
   unchanged in every mode; the safe area can only widen it.

   The two selectors stay in separate rules on purpose: a browser that does not
   know `:fullscreen` would discard the whole list, taking the emulated case —
   the one that only exists for such browsers — down with it. */
.renderer-surface--wasm:fullscreen .render-controls {
  padding-top: max(var(--render-control-inset), env(safe-area-inset-top, 0px));
  padding-right: max(var(--render-control-inset), env(safe-area-inset-right, 0px));
  padding-bottom: max(var(--render-control-inset), env(safe-area-inset-bottom, 0px));
  padding-left: max(var(--render-control-inset), env(safe-area-inset-left, 0px));
}

.renderer-surface--wasm[data-expanded="true"] .render-controls {
  padding-top: max(var(--render-control-inset), env(safe-area-inset-top, 0px));
  padding-right: max(var(--render-control-inset), env(safe-area-inset-right, 0px));
  padding-bottom: max(var(--render-control-inset), env(safe-area-inset-bottom, 0px));
  padding-left: max(var(--render-control-inset), env(safe-area-inset-left, 0px));
}

@media (max-width: 40rem) {
  .render-controls {
    --render-control-inset: 0.6rem;
    gap: 0.4rem;
  }

  .render-controls__telemetry {
    min-height: 1.55rem;
    padding: 0.32rem 0.5rem;
    font-size: 0.54rem;
  }
}

/* Landscape phones give the render view very little height; the elastic middle
   row absorbs it, and the edge inset tightens so both rows still fit. */
@media (max-height: 26rem) {
  .render-controls {
    --render-control-inset: 0.5rem;
  }
}

/* Touch keeps a comfortable target, and a tapped control must not stay in the
   hover state after the finger lifts. */
/* 2.75rem is the 44px a finger is expected to be given. The previous 2.35rem
   was under it, which is why the control read as something to aim at rather
   than something to press. */
@media (pointer: coarse) {
  .render-control {
    width: 2.75rem;
    height: 2.75rem;
    font-size: 0.74rem;
  }

  .render-control--reset {
    font-size: 1.55rem;
  }
}

@media (hover: none) {
  .render-control:hover:not(:focus-visible) {
    border-color: var(--render-control-border);
    background: var(--render-control-surface);
    color: var(--render-control-ink);
  }
}

/* Reduced motion drops the WebGPU layer, so the cadence line has nothing to
   report. The controls stay: they are navigation, not decoration. */
@media (prefers-reduced-motion: reduce) {
  .render-controls__telemetry {
    display: none;
  }

  .render-control {
    transition: none;
  }
}

/* Selection feedback for a node the graph will not open.
 *
 * Anchored under the way back, in the corner that shares its concern —
 * navigation the visitor just attempted. The telemetry line owns the opposite
 * corner, so the two can never meet however narrow the surface becomes, and
 * the toast takes no pointer: it reports, it is not a control. */
.graph-toast {
  position: absolute;
  z-index: 7;
  /* The toast is a sibling of the overlay, not a child, so the overlay's inset
     variable is out of scope here and the offsets are literal. 3.6rem clears a
     coarse-pointer control at the widest inset. */
  top: 3.6rem;
  left: 0.85rem;
  max-width: min(20rem, calc(100% - 1.7rem));
  padding: 0.6rem 0.8rem;
  border: 1px solid rgba(116, 139, 255, 0.22);
  border-radius: 0.75rem;
  background: rgba(2, 4, 15, 0.86);
  box-shadow: 0 0.4rem 1.4rem rgba(0, 0, 0, 0.28);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  pointer-events: none;
}

.graph-toast__title {
  margin: 0;
  color: rgba(226, 232, 255, 0.92);
  font-size: 0.82rem;
  line-height: 1.3;
}

.graph-toast__detail {
  margin: 0.15rem 0 0;
  color: rgba(190, 201, 244, 0.62);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.66rem;
  line-height: 1.35;
  letter-spacing: 0.04em;
  overflow-wrap: anywhere;
}

.graph-toast--private {
  border-color: color-mix(in srgb, var(--private), transparent 58%);
  background: color-mix(in srgb, var(--private), #02040f 94%);
}

.graph-toast--private .graph-toast__title {
  color: var(--private);
}

.graph-toast--private .graph-toast__detail {
  color: color-mix(in srgb, var(--private), white 20%);
}
